I see a lot of Jersey-based web services consisting of 1+ WebResources that have 1+ endpoints/methods like so:
package com.myws.fizz;
public class FizzResource {
#GET
#Path("/fizz/{id}")
public Response getFizzById(#PathParam("id") Long id) {
// ...etc.
}
#GET
#Path("/fizz")
public Fizz getFizzByFoo(Foo foo) {
// ...etc.
}
}
package com.myws.buzz;
public class BuzzResource {
#POST
#Path("/buzz")
public Response createBuzz(Buzz buzz) {
// ...etc.
}
}
I'm confused with what Jersey considers a "resource". Is there a relationship between a "resource" and a database? A table? A POJO?
When do you return a Response vs. a POJO? Look at my 2 getFizz methods above. When do I return a Fizz and when do I return a Response?
The term "Resource" isn't so much just a Jersey term, as it is a REST term. When dealing with REST, we have Resources and Representations. A resource can be anything, and in this context, it is some object located on the server, with a URL location. When a client requests for a resource, we send back a representation of it. You ask:
Is there a relationship between a "resource" and a database? A table? A POJO?
It could be a database (that's a thing). And we could simply represent it as a JSON object with the name of the database. Also it could be a table (that's a thing). And we could represent it as a JSON object with the name and column names. It could be a row in a table, and we could represent that with a JSON object with the column names as the keys and the row values as the JSON values. It could be a web page, an image, whatever. So hopefully you get the point that a resource can be anything. What we send back is its representation.
But the term resource is not just limited to returning something to a client request. The client could also send us a representation of a resource, to say create a new resource (POST) or change an existing resource (PUT). The client may send us a JSON representation of a row (resource) in our database.
When do you return a Response vs. a POJO? Look at my 2 getFizz methods above. When do I return a Fizz and when do I return a Response?
Returning Response allows you to fine tune the Response. When the client make a request, they always get back a response. Responses have headers, and entity bodies. When the return type of the resource method is Fizz, you are saying the entity body type will be a Fizz type. When the method returns, what actually happens is that the Fizz object isn't directly returned to the requesting client, all by itself. Behind the scenes, it gets wrapped in a Response that get's sent back to the client. The framework will set the headers it feels appropriate.
So whether we decide to return a Response or Fizz, it will get wrapped in a Response. And like I said, when we return a Response, it allows us to fine tune the Response, adding our own headers, status codes and such. For example, say someone make a POST. You could do something like
#POST
#Path("/buzz")
#Produces(...)
public Response createBuzz(Buzz buzz, #Context UriInfo uriInfo) {
int buzzID = // create buzz and get the resource id
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.path(Integer.toString(buzzId)); // concatenate the id.
return Response.created(builder.build()).build();
}
Basically what this does is create the resource, say in the database, and we get a return id. We can use the id to concatenate to the URI to the id, this will be the new resource location. In terms of the Response, .created(...) is saying that the status code should be 201 Created, and the value we pass to the created method is the location of the newly created resource. This location will be set as the Location header in the response. So lets say that the path to the POST request is http://blah.com/buzz. What we would send back is http://blah.com/buzz/100, where 100 is the buzzId, and this complete URL is how we will access the buzz resource say with a GET requesst to a resource method annotated with say #GET #PATH("/buzz/{id}")
In terms of a GET, with a Response, we could do
Fizz newFizz = fizzService.getFizz();
return Response.ok(newFizz).build(); // sends the Fizz as the entity body
This is really not so much different than just returning the newFizz from the method, because we aren't doing anything special with the Response. We're just saying the status code should be 200 OK and attaching the entity body. This is what normally happens with a successful GET request. So if we just return the Fizz, instead of a Response, in the case of a successful GET, the framework will implicitly attach a 200 OK status.
Personally, I prefer to return Responses, because of the fine tuning factor.
Related
at work we building an web application with java spring backend and vue frontend.
At the moment we uses 2 or 3 http response code to pass errors between frontend and backend.
If you call and endpoint with wrong parameters, you'll get an BAD_REQUEST. If some exception was thrown in the backend (which isn't related with the parameters) the backend returns an INTERNAL_SERVER_ERROR and if you pass some ids which aren't in the database the backend returns an NOT_FOUND.
This method has multiple problems:
we have no structure in error case which we can use to pass information to the user (frontend)
we want to indicate problems to the user, which can't be classified by HTTP response codes. For example if an external service isn't available, we want to pass the service name to the frontend. But I don't know if "SERVICE_UNAVAILABLE" fits here...
I found this already: https://www.baeldung.com/spring-response-status-exception
We could use the message field to pass detailed information about an error (specific error json in message field).
Is this a good idea?
Opinions?
T
You can certainly pass information back in this field but using ResponseStatusExceptions. Depending on how much information the frontend needs (e.g. if it's just surfacing a user friendly message to the user) this may be enough for your needs.
Another approach, if you want to use a custom object in the response (esp. per exception/response code), is using #ControllerAdvice and extending ResponseEntityExceptionHandler.
e.g. say you have a custom exception ExternalServiceUnavailableException which had some underlying ServiceInformation you could retrieve from it. Then you could do something like
public class ServiceInformation {
private final String name;
private final String status;
private final String statusMessage;
//snip
}
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({ ExternalServiceUnavailableException.class })
public ResponseEntity<Object> handleExternalServiceUnavailable(ExternalServiceUnavailableException ex, WebRequest request) {
final ServiceInformation si = ex.getServiceInformation();
return ResponseEntity
.status(503) // or whatever code you want
.body(si); // or map to some other object/format
// or use the constructor to supply headers etc.
}
}
When you throw a ExternalServiceUnavailableException this would result in a response body like
{
"name": "my-external-service",
"status": "timeout",
"statusMessage": "Service timed out after 30 seconds"
}
A more complete example of this can be found in the below article where the same custom error object is used for each of the exceptions of consequence as well as a default handler.
https://www.baeldung.com/exception-handling-for-rest-with-spring
This makes it easier for the frontend to interpret (as does your proposed approach) since there is only a single format to expect and parse, but you are free to return different response shapes per exception.
Edit: it's worth remembering that there are also response codes 502 (bad gateway) and 504 (gateway timeout) which can be used to indicate an external service is either unavailable or timing out. If these are appropriate you could just use appropriate ResponseStatusExceptions with a message set to include the service name (or other info). As above, it depends on what you need/want the fronted to receive.
I'd like to create a controller which will return Page object. I will need page Pageable to have with page number and its size (it cannot be larger than 50) and some variables which will be search criteria. If this was a POST request, it could look as follows:
public Page<SomeDto) getDto(#RequestBody #Valid RequestDto requestDto, Page pageRequest)
However, it is a GET request so #RequestBody cannot/shouldn't be used.
Additionally, I would like to have something akin to #Valid so that Spring will automatically reject bad request.
I'm wondering whether:
it is possible to and if so how to implement this and have already created Dto from values taken from URL (#PathVariable or #RequestParam) as if it was POST request which maps body to object.
I'd like to avoid using some kind of that code:
public String updateFoos(#RequestParam Map<String,String> allParams) {}
if what is described in 1st is not possible, what solution would be closest as to meeting those requirements?
You can have a HTTP body (and therefore a #RequestBody) for every HTTP requests no matter what HTTP method is used. However, it is not a good REST API design to use a HTTP body for GET request (cf. https://martinfowler.com/articles/richardsonMaturityModel.html)
You can do bean validation with #PathVariable or #RequestParam arguments, you just have to put the annotation you want next to these annotations
I have this api:
#Path("test")
#GET
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Parameter performTest(Parameter in) {
System.out.println(in);
}
but the in always returns null. I can change #GET to #POST and it works but I'm not really performing an create or update so using post seems odd.
Is there a way to get the body with a GET request with jersey?
TL;DR The correct solution is to use POST.
"I can change #GET to #POST and it works but I'm not really performing an create or update so using post seems odd"
Why is that odd? POST is not limited to create/update operations.
The specification (RFC 7231, section 4.3.3. POST) says:
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. For example, POST is used for the following functions (among others):
Providing a block of data, such as the fields entered into an HTML form, to a data-handling process;
Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group of articles;
Creating a new resource that has yet to be identified by the origin server; and
Appending data to a resource's existing representation(s).
To paraphrase, POST means "here's some data, please process it for me".
Sure, "process" often means "store", as in create/update, but that is not the only way to process data.
In your case, "process" means "run test, using these parameters".
I've been having a problem regarding using AJAX with Spring MVC. I have a form which has a lot of fields, and each field retrieves data depending on the associated button that was clicked.
So, each one of my buttons needs to call an AJAX request. Each response will be displayed on the associated field.
I wonder if it is possible to call a different method in my Spring controller once I clicked on a different button?
In other words, I want to make multiple ajax requests to the same controller where each request will call a different method in that same controller.
See this example :
// when get account detail is clicked it will call this method
#RequestMapping(method=RequestMethod.POST)
public #ResponseBody String getAccountDetails(#RequestParam(value="accountid") String accountid){
return somefunct.getAccountDetails(accountid);
}
// when get account summary is clicked it will call this method
#RequestMapping(method=RequestMethod.POST)
public #ResponseBody String getAccountSummary(#RequestParam(value="accountid") String accountid){
return somefunct.getAccountSummary(accountid);
}
/* when submit button is clicked... Form is submitted for saving*/
#RequestMapping(method=RequestMethod.POST)
public String submitForm(){
// save here
return "myform";
};*/
Currently, I can have only one AJAX request. How can I modify this code so that I can have different functionality for different AJAX requests?
First, consider that when you retrieve data from a server without modifying the state of that server, the commonly accepted standard is to use the HTTP GET method, not POST. Thus, for your first two methods, you are misusing the HTTP Methods.
Second, you can map individual URL patterns to a specific method using the value property of the RequestMapping annotation.
Third, the most RESTful way to represent your account details resource is to use the PathVariable annotation and include your identifying accountid in the actual path:
#RequestMapping(value="/account/{accountid}/details", method = RequestMethod.GET)
public #ResponseBody String getAccountDetails(#PathVariable(value="accountid") String accountid){
return somefunct.getAccountDetails(accountid);
}
Next, you can represent your account summary using a different URL pattern where the URL is built like a tree, where the first two parts of the path are once again "Account" and the accountid:
// when get account summary is clicked it will call this method
#RequestMapping(value="/account/{accountid}/summary", method=RequestMethod.GET)
public #ResponseBody String getAccountSummary(#PathVariable(value="accountid") String accountid){
return somefunct.getAccountSummary(accountid);
}
Now, your submit method, on the other hand, has side effects. This is just a fancy way of saying that the state of your server will be different at the end of this request, and any GET requests made to that resource will be different than they were prior to the change. The appropriate HTTP method to use when modifying a resource or adding a resource to a collection is the HTTP POST Method. When replacing a collection, the HTTP Method PUT is the generally accepted method of choice.
Another differentiating factor between PUT and POST is that PUT is idempotent, meaning that the same request repeated over and over again doesn't change the state on the server. If hitting the same request multiple times creates more records, then use POST.
Lastly, this request can be mapped to a URL as well. In the example below, I've assumed you are creating a new Account record and inserting a new record in the collection of accounts in the database. Thus, I've used POST. I also modified your parameter list to use PathVariable to take the accountid from the URL path, and I added a RequestBody annotation so that you can send an object in the body of the request, which could be deserialized into a Java object:
/* when submit button is clicked... Form is submitted for saving*/
#RequestMapping(value="/account/{accountid}", method=RequestMethod.POST)
public String submitForm(#PathVariable String accountid, #RequestBody Account account){
// save here
return "myform";
}
For more information on Spring MVC, please check out the Spring documentation on Spring MVC.
I've create a simple test case with Jackson 1.9 and Spring 3. My goal was to test how easy was to use both of them to generate JSON results. My problem is that I get an error 406
So far, it seems that you only need to configure Spring and the code is really straightforward . My Spring configuration file is just:
<context:component-scan base-package="com.goldengecko" />
<context:annotation-config/>
<mvc:annotation-driven />
And my controller:
#Controller
public class TestsController
{
#RequestMapping(value = "/tests", method=RequestMethod.GET)
public #ResponseBody Item getBooks() {
return new Item();
}
}
The Item class is just:
public class Item {
private String name;
public Item() {
name = "Test name";
}
private String getName() {
return name;
}
private void setName( String name ) {
this.name = name;
}
}
I made sure I added the jackson-core-asl.jar and jackson-mapper-asl.jar.
From everything I read, it's just that: you don't seem to need to worry about setting a content-accepted in the request, just open a normal Chrome browser and request that service.
That's where I get a 406.
I created a simple jQuery getJSON call, with the same result.
Do you know what can be wrong? So far there are few things to fail: the code seems to be the right approach and the Jackson files required by Spring are there.
Per section 10.4.7 at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html:
The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
Unless it was a HEAD request, the response SHOULD include an entity containing a list of available entity characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content-Type header field. Depending upon the format and the capabilities of the user agent, selection of the most appropriate choice MAY be performed automatically. However, this specification does not define any standard for such automatic selection.
Note: HTTP/1.1 servers are allowed to return responses which are not acceptable according to the accept headers sent in the request. In some cases, this may even be preferable to sending a 406 response. User agents are encouraged to inspect the headers of an incoming response to determine if it is acceptable.
If the response could be unacceptable, a user agent SHOULD temporarily stop receipt of more data and query the user for a decision on further actions.
So it sounds like perhaps you do need to set the acceptable content types on the receiving side.