I'm trying to understand how to create and modify links in Spring HATEOAS.
For example, say I have two collections, one at api/users and another at api/event. I would like to associate a user api/user/56 with an event api/event/21. For arguments sake this is a many-to-many - a user may attend many events, an event may have many users.
As I understand it, the restful way of doing this is to use the URIs as primary keys, so I might post the following to api/user/56/events;
{
attends: "http://localhost:9090/api/event/21"
}
The endpoint then needs to be able to parse that URL and extract the ID (in this case 21) and the controller (EventController.class) so that I can persist this.
Question 1: Is this the correct way of dealing with relationships in Spring Hateoas in terms of the REST API?
Question 2: How can I resolve this url in a controller to a usable handle on the data (for example a reference to the appropriate controller/method, a primary key, etc)
Research
RestTemplate can be used to request the data from the controller inside the request mapped method, like so;
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<EventResource> response = restTemplate.getForEntity(attendsUrl, EventResource.class);
EventResource eventResource = response.getBody();
However I don't believe that eventResource should return an Id field as part of the data - it's not very restful and this would be exposed on the API. One approach is to have a parameter "includePK=true" but again this doesn't feel right - it's just hiding the problem. Moreover the idea of the server making requests to it's own API in this manner seems circuitous.
Update
There is an open question for this here https://github.com/spring-projects/spring-hateoas/issues/292. Based loosely on some of the comments (by user kevinconaway) from that issue I have made a quick util class that offers an easy solution here: SpringHateoasUtils. The solution boils down to;
String mapping = DISCOVERER.getMapping(targetClass, targetMethod);
UriTemplate template = new UriTemplate(mapping);
//values is key/value map of parameters that the referenced method accepts
Map<String, String> values = uriTemplate.match(uri);
SpringHateoasUtils makes this slightly nicer but it still feels like it should be a feature. I'll seek to get something in the spring code for this - when it's clear what is happening with this I'll answer this question.
Look at the answer here:
POSTing a #OneToMany sub-resource association in Spring Data REST
Question 1) Yes this is how you post links/relations. With URIs.
Question 2) The URI of the resource actually IS its ID from the client's perspective. The server internally automatically resolves this URI into the actual model instance with
org.springframework.data.rest.core.UriToEntityConverter.convert(...)
Related
I am building the restful web service. For the put request, I first find the testBean with the id in the pathvariable. If it does not exist, then I create a new one. I am wondering if it is right to create a new one here, or I should throw the exception. Because id is auto increment, if I create a new TestBean, the id saved in the db is different from the one from the url path parameter.
#PutMapping("/Test/{id}")
public TestBean updateTestBean(#PathVariable long id, #RequestBody TestBean newTestBean) {
return testBeanService.getTestById(id)
.map(testBean -> {
testBean.setBRR(newTestBean.getBRR());
testBean.setModifiedDate(newTestBean.getModifiedDate());
return crewsBeanService.saveTestBean(testBean);
})
.orElseGet(() -> {
newTestBean.setId(id);
return testBeanService.saveTestBean(newTestBean);
});
}
I'd always prefer to keep PUT method idempotent. Idempotency can be explained as how many times you apply a certain "operation", the result will be the same as the first time. Since REST is just a style, it's up to you, but I will always question to me if it makes sense to keep the operation as PUT or POST.
What if the client of your service is impatient and access your PUT service multiple times while the first request is being served?. You may end up creating two users. So throwing an exception is meaningful if the ID doesn't exist.
It can be 400 or 404, I don't prefer 404 but prefer 400 because of the following reasons,
1) It confuses the client of your APIs if the resource is wrong or the ID they are using is wrong.
(You can always differentiate in your error response and provide meaningful information, but still, I don't prefer!)
2) By using 404,
you're telling the user the problem could be permanent or temporary
,for instance, say your service is not properly registered with discovery server(eureka) or is crashed, the discovery server will send 404 until you fix the problem.
By using 400,
you're asking the user to try with different input, in this case, with a different ID. This is permanent...
as you said id is auto-increment and the client cannot decide the value, so until the user fixes the problem by going back and request your POST service for a new ID, the request is "BAD" and cannot be processed.
Based on Single Responsibility Principle, you should have methods which are doing only one thing. So for your question, you need 2 methods for each request:
GET - asking the server for an object, in your case TestBean.
POST - save new objects (you don't need an id for these).
And in your front end application you could use the GET to ask the server if it have the requested object, and if not, maybe you can add a form which on submit will make the POST request with the data provided in the form fields.
PUT should only be responsible for updating a record. If the id of your bean doesn't exist, you will have an exception on your persistence layer. You can catch that exception on your API and return one of the 400's response code, such as BAD REQUEST.
For creation you should use POST, an id should not be provided in that case
This would be the RESTful way of doing this.
404 is the correct return code for a PUT to a non-existent resource, because the URL used does not address an extant resource.
If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
If the server desires that the request be applied to a different URI, it MUST send a 301 (Moved Permanently) response; the user agent MAY then make its own decision regarding whether or not to redirect the request.
https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Newbie question...
I'm building my first Spring Boot restful service and want to support a GET call that returns a collection of entities. like:
/api/customers/
However, for certain consumers -like a list page in a web UI - they would only need a subset of the customer entity properties.
I'm thinking that I could add request parameters to my GET call to set the consumers specific field requirements, like
/api/customers/?fields=id,name,address
But what's the best way of implementing this inside the Java restful controller?
Currently in my rest controller the 'GET' is request mapped to a Java method, like
#RequestMapping(value="/", method= RequestMethod.GET)
public Customer[] getList() {
Customer[] anArray = new Customer[];
....
return anArray;
}
Is it possible to somehow intervene with the default Java to Json response body translation so that only the required properties are included?
TIA
Adding fields parameter is a good idea, best practice according to http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#limiting-fields
How to leave fields out?
1) Set them to null, possibly in a dedicated output class annotated with #JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
Or
2) Use SimpleBeanPropertyFilter See a good step by step tutorial here 5. Ignore Fields Using Filters
I am currently working on a monitoring application using Spring Cloud Sleuth. Currently I try to collect as much information about my requests as possible.
To keep the approach as scalable as possible I use GenericFilterBeans and HandlerInterceptorAdapter to access information from the requests sent to the REST-API.
I am struggling with getting parameters of a REST-call where the parameters are mapped from the URL like in the following code snippet:
#RequestMapping(
value = {"/{service}/{route_id}/book", "/accounting-core-service/{service}/{route_id}/book"},
method = RequestMethod.GET)
#ResponseBody
public ModelAndView book(#PathVariable(value="service") String serviceName,
#PathVariable(value = "route_id") int routeId,
HttpServletResponse response) {
/*Do something*/
}
The question is not, whether it is good practice or not to write it like so. The question is whether there is an approach similar to Filter or Interceptor (or the proper use of them) to access those parameters.
A requirement is, that it can be applied easily to an application by adding very few lines of code. Annotating every Method call manually or manually inserting the code to write the parameters into the trace from within the method is not feasible for me.
If you need more information feel free to ask. I will provide you with all information you need to help me with my problem.
Although not officially supported (as it's not written in the reference documentation), Spring MVC holds that information as request attributes.
You could create your own HandlerInterceptor, ordered right after the Sleuth one, and get that information from the request like this:
// "/{service}/{route_id}/book"
String matchingPattern = (String) request
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// "service" => "fooService", "route_id" => "42"
Map<String, String> templateVariables = (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Note, the template variables are already decoded at that point, which is probably what you want anyway.
You can create a Filter that intercept all the requests.
For each request you can retrieve also this informations:
query parameters
body of request
url
header parameters
You can save all this data as you need.
This is the best way you can do that.
If you need to group all urls like /{service}/{route_id}/book in a "family" of urls you can do that splitting the url and check if it is part of the family, but when a new family is added in your code you need to update the filter (or configure something in an external file or database).
As the title says. For context, although not terrible important, I'd like to trigger a WebSocket notification in the AfterSave event listener, and I'd like the resource or collection URI to correspond to the subscription endpoint. Of course, I could hard code these values, but then I'm losing some benefit of using SDR.
So, does Spring offer a way to retrieve this information, the collection or entity URI, solely based off of the Object that the event listeners receive?
If I'm understanding your question correctly, you can do this by autowiring EntityLinks and then getting the link like this:
Link link = entityLinks.linkToSingleResource(MyResource.class, id);
String href = link.getHref();
With Spring MVC, I know how you set the RequestMapping in every controller and method/action.
But what if I wanted this to be configurable, so for example I the following controllers:
BlogController
- with methods for listing blogs entries, single entry, new, update, etc.
ArticleController
- with methods for listing articles entries, single entry, new, update, etc.
Now in my application, the administrator can setup 2 blogs for the webiste, and 1 article section so the urls would be like:
www.example.com/article_section1/ - uses ArticleController
www.example.com/blog1/ - uses BlogController
www.example.com/blog2/ - uses BlogController
Maybe after a while the administrator wants another article section, so they just configure that with a new section like:
www.example.com/article_section2/
This has to work dynamically/on-the-fly without having to restart the application of course.
My question is only concerned with how I will handle url mappings to my controllers.
How would this be possible with Spring MVC?
I only know how to map urls to controllers using #RequestMapping("/helloWorld") at the controller or method level, but this makes the url mappings fixed and not configurable like how I want it.
Update:
I will be storing the paths in the database, and with the mapping to the type of controller so like:
path controller
/article_section1/ article
/blog1/ blog
/blog2/ blog
..
With the above information, how could I dispatch the request to the correct controller?
Again, not looking to reload/redeploy, and I realize this will require more work but its in the spec :)
Would this sort of URL mapping work for you?
www.example.com/blog/1/
www.example.com/blog/2/
If yes, then that's easy: Spring 3 supports path variables: http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-ann-requestmapping-advanced
Alternatively, you can create a generic request mapping and your own sub-dispatcher that reads a config file, but I think that's probably more work than it's worth.
Truly changing the request mappings at runtime might be hard (and not really recommended, since small errors can easily occur). If you still wish to do it, perhaps JRebel, and more specificly LiveRebel can be interesting for live redeployment of code and configuration.
Otherwise, like other posts suggested, RequestMappings supports wildcards, the limits of this should be clear after a quick read of the official documentation.
Try using with #RequestMapping wild cards as below:
#RequestMapping(value="/article_section*/"}
public void getArticle(....){
//TODO implementation
}
#RequestMapping(value="/blog*/"}
public void getBlog(....){
//TODO implementation
}
Hope this helps!!!
Also another solution might be to create a custom annotation that holds the already defined path on the #RequestMapping and also the new one to apply, let's say #ApiRestController.
Then, before the Spring context loads, the #Controller classes can be changed to have their annotation values changed at runtime by the new one (with the desired path). By doing this, Spring will load the enhanced request mapping and not the default one.
Created a small project to exemplify this for someone that needs this in the future https://gitlab.com/jdiasamaro/spring-api-rest-controllers.
Hope it helps.
Cheers.
doesn't this work?
#RequestMapping("/helloWorld*")