Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does jersey service return if @Produces annotation missing?

I was begun learn jersey for development restful web services.

As I noticed in most of the examples uses following annotations:

@Consumes

defines format of input parameters

@Produces

defines format of output parameters

But in real code I see method which looks like this:

@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/login")
public Response login(@FormParam("login") final String username, @FormParam("password") final String password){...}

I see that this method uses POST HTTP method. Parameters userName and password will have form according @Consumes(MediaType.APPLICATION_FORM_URLENCODED). and I see URL to execute this method.

But I don't understand what this method returns. Which format?

like image 824
gstackoverflow Avatar asked Dec 26 '22 01:12

gstackoverflow


1 Answers

I just want to clarify that "By Default Jersey produces "application/octet-stream" if it's not specified" is not entirely true. There's actually a lot of complexity going on behind the scenes, that determines the final Content-Type. As stated in the spec:

Note that the above (actually below :-) renders a response with a default media type of application/octetstream when a concrete type cannot be determined.

But, like I said, there is a complex algorithm that goes into determining this "concrete type". And not many cases I've tested will return application/octet-stream. It goes as follows (this is directly from spec. You can try and make head or tails of it, but it's not for the layperson):

3.8 Determining the MediaType of Responses

In many cases it is not possible to statically determine the media type of a response. The following algorithm is used to determine the response media type, Mselected, at run time:

  1. If the method returns an instance of Response whose metadata includes the response media type (Mspecified) then set Mselected = Mspecified, finish.

  2. Gather the set of producible media types P:

    • If the method is annotated with @Produces, set P = {V(method)} where V (t) represents the values of @Produces on the specified target t.
    • Else if the class is annotated with @Produces, set P = {V (class)}.
    • Else set P = {V (writers)} where 'writers' is the set of MessageBodyWriter that support the class of the returned entity object.
  3. If P = {}, set P = {'*/*'}

  4. Obtain the acceptable media types A. If A = {}, set A = {'*/*'}

  5. Set M = {}. For each member of A; a:

    • For each member of P; p:
      1. If a is compatible with p, add S(a; p) to M, where the function S returns the most specific media type of the pair with the q-value of a and server-side qs-value of p.
  6. If M = {} then generate a NotAcceptableException (406 status) and no entity. The exception MUST be processed as described in Section 3.3.4. Finish.

  7. Sort M in descending order, with a primary key of specificity (n/m > n/* > */*), a secondary key of q-value and a tertiary key of qs-value.

  8. For each member of M; m:

    • If m is a concrete type, set Mselected = m, finish.
  9. If M contains '*/*' or 'application/*', set Mselected = 'application/octet-stream', finish.

  10. Generate a NotAcceptableException (406 status) and no entity. The exception MUST be processed as described in Section 3.3.4. Finish.

You can see it's not as easy as saying it will always default to application/octet-stream. Simple example

@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createCustomer(@FormParam("id") int id, 
                               @FormParam("name") String name) {
    return Response.ok("OK I GOT IT").build();
}

The above will return Content-Type: text/plain

Say you create a Customer object and return it

@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createCustomer(@FormParam("id") int id, 
                               @FormParam("name") String name) {
    Customer customer = new Customer(id, name);
    return Response.ok(customer).build();
}

From what I have tested, it will return Content-Type: application/xml and yes the body content will be xml.

Now if we send the request with an Accept header of application/json, we will get a response header of Content-Type: application/json, as yes the body content will be json. This is where Content Negotiation plays a factor

If we just responded with a 201 Created, which is very common in POST/create requests

@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createCustomer(@FormParam("id") int id, 
                               @FormParam("name") String name) {
    return Response.created(someNewUri).build();
}

there will be no Content-Type response header, as there's no content.

Now some of the example above aren't great REST princial examples, but it goes to show, that a lot goes in to determining the media type, if we don't explicitly set it with @Produces. You have to consider, the body of the response, available MessageBodyWriters, you have Content Negotiation to factor in, and whatever else is in that mumbo-jumbo from the spec. (Note: I've bolded the'Content Negotiation' link because it is a concept you should really get familiar with when working with REST. It actually plays a big part with JAX-RS/Jersey).

So really the answer to your question is it depends. But hopefully you've gained some extra knowledge from this post :-)

like image 126
Paul Samsotha Avatar answered Jan 05 '23 17:01

Paul Samsotha