So let's say I have an existing application that has two endpoints /people and /pants. Calling GET /people returns:
[
{
"name":"john",
"age":37,
"pants":[
{
"color":"green",
"brand":"levis",
"size":"medium"
},
{
"color":"indigo",
"brand":"jncos",
"size":"medium-with-huge-legs"
}
]
},
{
"name":"june",
"age":23,
"pants":[
{
"color":"pink",
"brand":"gap",
"size":"small"
}
]
}
]
If i were to use Spring Data Rest and call GET /person i'd receive something like:
{
"_links":{
"next":{
"href":"http://myapp.com/people?page=1&size=20"
},
"self":{
"href":"http://myapp.com/people{&page,size,sort}",
"templated":true
},
"search":{
"href":"http://myapp.com/people/search"
}
},
"_embedded":{
"people":[
{
"name":"john",
"age":37,
"_links":{
"self":{
"href":"http://myapp.com/people/john"
},
"pants":{
"href":"http://myapp.com/people/john/pants"
}
}
},
{
"name":"june",
"age":23,
"_links":{
"self":{
"href":"http://myapp.com/people/june"
},
"pants":{
"href":"http://myapp.com/people/june/pants"
}
}
}
]
}
}
Let's say I have a bunch of existing clients that I don't want to have to change - is there any way to disable the hypermedia portions of the response in some cases (say Accept="application/json") but enable it them for others (Accept="hal+json")?
Thanks!
Updated
Okay - so it appears that much to my chagrin, what I'm looking to do is not supported. I understand why Spring Data Rest is strongly leaning toward Hypermedia... but I don't buy that providing the capability to "disable" hypermedia based on a header thus providing more options is a bad thing.
That aside, I'm a bit unsure of how to actually achieve this via my own Controllers. If I create a Controller and attempt to override the /people RequestMapping with produces = "application/json" I am able to get the "raw" json back with Accept="application/json" but if I pass Accept="application/hal+json"` I get a 406 with "Could not find acceptable representation". It looks like the SDR resource mappings aren't mapped with a content type ... any suggestions?
The short answer is, you can't use spring-data-rest without hateoas. If you want to build your web service without hateoas, you'll have to write your own controllers (which can still use spring-data repositories).
Quoting Oliver Gierke in this SO post:
Actually my whole point is: the server is just doing decent REST. If
that breaks the client, it's the client that needs to be fixed
(tweaked). So the hypermedia aspect is a fundamental one to Spring
Data REST and we're not going to back out of that. That's probably not
satisfying in your concrete situation but should answer the question
at least :). – Oliver Gierke
Related
Do you know any "best practice" to design a REST method to alter the order of a small collection?
I have a collection exposed at "GET /api/v1/items". This endpoint returns a JSON array and every item has a unique id.
I was thinking on create "PATCH /api/v1/items" and send an array of ids with the new order. But I wonder if there is any alternative or design pattern to accomplish this task properly.
Following the REST Uniform Interface constraint, the HTTP PUT and PATCH methods have to stick to the standard semantics, so you can do that with either one in the following way:
With PUT, clients can upload a whole new representation with the order they want. They will request GET /api/v1/items, change the order as they need, and submit it back with PUT /api/v1/items.
With PATCH, clients can send a diff document which performs the order change as they need. You can use a format like json-patch and clients perform the change with the move operation and array paths.
Be aware that neither of these are design patterns or best practices. They are simply how the PUT and PATCH methods are supposed to work. Ideally, this should work on any RESTful application implementing the GET, PUT and PATCH methods correctly for the resource at that URI, and that's the beauty of REST. If you do it the right way, you only have to do it once and clients can generalize for everyone. For instance, a client can choose to do it the PUT way with small collections, and the PATCH way for larger ones.
Both your idea to use PATCH with an id array, and the answerfrom #dit suggesting to do it with PUT aren't really RESTful because they are breaking up with the standard semantics: yours for not using a delta format, his for doing partial updates with PUT. However, both those options can be RESTful if done with POST. POST is the method to go for any action that isn't standardized by the HTTP protocol, so you can do anything you want with it, but you have to document how exactly to do it.
So, it's up to you. If you are concerned at all with being RESTful and your application has long term goals -- I'm talking years or even decades -- I'd say to go for a uniform implementation of the PUT and PATCH methods as suggested first. If you prefer a simple approach, use yours or dit's idea with POST.
ok, I had a similar problem and will try to explain how I solved it.
my bike tour has about 5 stations. Every station has an unique ID and order number:
stations": [
{
"uid": 1,
"order": 1
},
{
"uid": 2,
"order": 2
},
{
"uid": 3,
"order": 3
},
{
"uid": 4,
"order": 4
},
{
"uid": 5,
"order": 5
}
]
Every time if the order of single item was changed (drag and drop) i send REST request to my webservice.
Suppose we want to move the station uid=3 one position down. Station with uid=3 goes down and station with uid=4 goes up. So my PUT request looks like this:
...myapplication.com/api/changeorder?station=3&direction=down
Now, on the server side I just have to find items affected by this move down action and update their order in my database. My REST webservice sends OK if update was successful.
In my case it was not necessary to send the new sorted list, cause my UI was always changed by drag and drop action.
Since I was not going to promote changes in the current API, I have used PUT method.
I was implementing a reordering of entities called "Slider". dotnet core
public class SliderMoveDto
{
public MoveDirection MoveDirection { get; set; }
public int CurrentSequence { get; set; }
}
public enum MoveDirection
{
Down = 0,
Up = 1
}
Implementation:
public async Task UpdateAsync(SliderMoveDto sliderMoveDto, int sliderId)
{
var allSliders = (await _sliderRepository.GetAsync(null, col => col.OrderBy(s => s.Sequence))).ToList<Slider>();
int currentIndexOfSliderInList = allSliders.FindIndex(s => s.Sequence == sliderMoveDto.CurrentSequence);
int indexToSwap = sliderMoveDto.MoveDirection == MoveDirection.Up
? currentIndexOfSliderInList-1
: currentIndexOfSliderInList+1;
SwapItems<Slider>(allSliders, currentIndexOfSliderInList, indexToSwap);
await RecalculateOrderOfSliders(allSliders);
}
Auxiliary methods:
public static void SwapItems<T>(IList<T> list, int indexA, int indexB)
{
T tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
}
private async Task RecalculateOrderOfSliders(List<Slider> allSliders)
{
for (int index = 0; index < allSliders.Count; index++)
{
int idOfSlider = allSliders[index].Id;
var sliderToUpdate = await _sliderRepository.GetByIdAsync(idOfSlider);
sliderToUpdate.Sequence = index + 1;
_sliderRepository.Update(sliderToUpdate);
}
await _unitOfWork.CommitAsync();
}
I have grails running a REST API and using version numbers in the URL like so: https://api.mycompany.com/v2/metadata. I need to change the parameters of one of the endpoints, so I'm bumping the version up to v3. Only one controller is affected, so I would like to delegate the remaining calls back to the controllers for v2 without having to copy/paste everything again. Here's the relevant section of my UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/v3/widget"(controller: "v3widget")
"/v3/$otherResource" {
// does not work, but illustrates what I want to happen
uri = { "/v2/" + params.otherResource }
}
// rest of my file...
"/v2/metadata"(controller: 'metadata')
...
What's the correct way to do this? I'm using grails 2.2.5
I would use a variable in the uri path, and instead of your example you would have the following mappings:
class UrlMappings {
static mappings = {
"/$apiVersion/widget"(controller: "v3widget")
"/$apiVersion/otherResource"(controller: "otherResource")
// rest of my file...
"/$apiVersion/metadata"(controller: 'metadata')
...
And then you could check for the value in controller:
class OtherResourceController {
def index(){
if(params.apiVersion == 'v2') {
...
} else {
...
}
}
}
The example here is checking for the string value, but you could go a bit deeper and actually convert the string value into internal api version enum representation which might be easier to manage. You could also do that in filter.
This way you can increment the logic changes and api will have a nice fallback, will delegate to default version.
But it gets really curly when you have couple of api versions layered one on the other.
The solution I found that works relies on the fact that wildcard mappings can also accept other regular expressions:
class UrlMappings {
static mappings = {
// v3 specific
"/v3/widget"(controller: "v3widget")
// v3/v2 common
"/v[23]/$otherResource" {
// normal mappings go here
}
// v2 specific
"/v2/metadata"(controller: 'v2metadata')
...
This solution works well since I don't have to repeat any mappings, and it's clear what's different between v2 and v3 resources.
In rails there's an easy way to add a collection end point to routes. e.g.
resources :books do
member do
get 'publisher' # /books/id/publisher
end
collection do
get 'total_count' # /books/total_count
end
end
Is there a similar way to map the total_count endpoint in Grails? The example here ( http://grails.org/doc/2.3.1/guide/single.html#urlMappings ) only shows a member route.
"/books"(resources: "book") {
"/publisher"(controller:"publisher")
"/total_count"(controller: "publisher") // ??? can this work?
}
I am currently using Grails 2.3.4.
It was simpler than I thought though if there's a more canonical way of solving this I'd appreciate the feedback.
Basically I defined the collection endpoint before the resource endpoint.
class UrlMappings {
static mappings = {
"/books/total_count" (controller: "Book", action: "totalCount", method: "GET")
"/books" (resources: "Book")
}
}
So far it appears to be working.
Right now, the Ember Data RESTAdapter is making this call:
GET /workshops
I want to make this call:
POST /scripts/server/api_call.php
{
"http_verb": "GET",
"endpoint": "special_namespace/workshops",
"data": {}
}
I'm doing stuff like session management, authorization, and OAuth signing in the api_call.php script, which makes the actual RESTful request and returns the result.
What are the steps to extend the RESTAdapter to do this?
I think you'd have to override the serialize and buildURL methods. You could also override the find call directly and bypass all of the unnecessary Ember-Data calls.
But in my opinion, the RESTAdapter is incredibly complicated nowadays. Given that your API is so different, I think you would be better off writing your own adapter from scratch. My custom REST adapter is about 100 lines long, and 20 of those are just $.ajax options. For instance, your find call could be as easy as:
find: function(store, type, id) {
return new Ember.RSVP.Promise(function(resolve) {
var data = {
http_verb: 'GET',
endpoint: 'special_namespace/' + type.typeKey.pluralize(),
data: {}
};
$.post('/scripts/server/api_call.php', data, function(response) {
resolve(response);
});
});
}
I've recently started a project using Luracast Restler. It seems a very simple and effective way to set up a REST API. With very little code, I was able to provide CRUD services for my Category and Product resources.
My GET methods look like this:
class Categories
{
function get($id=NULL) {
if (isset($id))
{
// return category details for $id.
}
else
{
// return all categories.
}
}
}
class Products
{
function get($id=NULL) {
if (isset($id))
{
// return product details for $id.
}
else
{
// return all products.
}
}
}
Clients can get the details of the "books" category using:
http:api/categories/books
or all categories using:
http:api/categories
Same for products. One product:
http:api/products/123
All products:
http:api/products
So far so good.
Now I want to progress to something slightly more involved. I want to give my clients access to the products in a category.
I want my URI to be:
http:api/categories//products
E.g.
http:api/categories/books/products
and from there, I want to offer:
http:api/categories//products/
E.g.
http:api/categories/books/products/123
This gives my client the ability to transfer from one resource to another using a progressive series of links, which I see as a core principle of REST.
But I can't see a way of achieving this with Restler. I've seen some mention of JavaDoc comments being used to specify URI mapping, so I tried this:
class Products
{
/**
* url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
But this doesn’t work. Restler doesn’t seem to take any information from the comment; it implicitly creates the URI route based on class name and function name.
Can anyone help? Am I missing something? Any advice would be much appreciated.
Everything is fine in the example above and what you are trying to achieve except one simple mistake that stopped it from working!
Your PHPDoc comment is missing #
Change your code as follows
<?php
class Products
{
/**
* #url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
Also take a look at the related question
How do you organize Luracast Restler classes to create related route endpoints?