My service method Produces one of this MediaTypes it may produce pdf or excel file or other.
#Produces({"application/pdf","application/vnd.ms-excel"...
My Question
My service returns response type with application/pdf always even if it produces excel. Why?
Than I rearranged MediaTypes.
#Produces({"application/vnd.ms-excel","application/pdf",...
Now it's giving type application/vnd.ms-excel for all responses,again Why?
I am using com.sun.jersey API for client and getting type by the use of
clientResponse.getType()
Probably I think I misunderstood the concept of #Produces annotation.
Please Clarify.
Following is code of my Service method.
response = Response.ok((Object) file);//file is Object of File
response.header("Content-Disposition","attachment; filename="+filename);
//filename can be a.pdf b.xlsx etc
return response.build();
JAX-RS methods should base the preferred content type on the value of the Accept header of your request. Failing that, it should default to the first specified.
While the JAX-RS spec is somewhat vague on the subject, the Jersey documentation is very clear in describing the selection mechanism.
As it said in the documenation:
#GET
#Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
...
}
The doGetAsXmlOrJson method will get invoked if either of the media types "application/xml" and "application/json" are acceptable. If both are equally acceptable then the former will be chosen because it occurs first.
Also, you can use quality factor for specifying which media type is more preferable:
#Produces({"application/xml; qs=0.9", "application/json"}).
In any way, if you want to be sure about which media type is used, you should divide your methods into two different signatures.
The #Produces annotation is used by the JAX-RS implementation to bind the incoming request to one of your Resource Methods, based on the accept header of the request.
You can set the exact type of the response in the returned Response object using ResponseBuilder#type(MediaType) if you want to enforce one media type in particular.
If you want to match the accept header of the incoming request ("application/vnd.ms-excel" vs "application/pdf" in your case), you can retrieve that header by adding a parameter annotated with #HeaderParam("accept") in your Java method.
HTH.
Related
I have a question in regards to the consumes and produces part of the #RequestMapping. I have an endpoint that I want to accept both JSON and XML and return JSON when JSON is passed in and return XML when XML is passed in. Is there anything special that I have to do to make this work?
Sample code is listed below.
#RequestMapping(value = "/something", method = PUT,
consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE},
produces = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public SomeObject updateSomeObject(SomeObject acct) {
return doStuff(acct);
}
Will this work the way I'm expecting or do I need two endpoints updateSomeObjectXML and updateSomeObjectJson to handle both cases?
Thanks,
Mike
The article from the Spring blog - Content Negotiation using Spring MVC - provides details on how content negotiation works with Spring MVC, in brief if you want the same endpoint to handle XML and JSON, your mapping is correct, to summarize from the article:
Use path extension - you can send a json to /something.json and xml to /something.xml and expect the same thing on the way back
Use the Accept header, use a value of application/json or application/xml and use Content-Type to specify the submitted media type.
Short answer:
Annotate the method with #ResponseBody, and the method parameter with #RequestBody, and it will work (no need for 2 methods).
Explanation:
First, produces and consumes attributes are used to narrow the mapping types. By default the first HttpMessageConverter found, that matches the media type requested, will be used.
Second, client requests a media type by giving the media type in:
- Accept request header
- URL sufix (http: //....//some .xml => "application/xml" media type requested)
- URL format parameter (.../some?format=xls)
Third, produces in combination with #ResponseBody will produce the object in the requested media type (nice for GET requests, when you need to send something back to the client), and consumes in combination with #RequestBody will consume the object with the requested media type (nice for POST requests, when you need to get something from the client).
Four, when #ResponseBody not used, HttpMessageConverters are not used. Rather ViewResolvers kick in and produce a view (HTML, PDF...), and the return type should follow the rules that accompany ViewResolvers (check default view resolver and InternalResourceViewResolver for more).
Hope it helps.
Other sources:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#consumes--
http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
Well,
consumes/produces takes String[] as a parameter (see RequestMapping from Spring's documentation) so I believe it will work. You can also try headers = "content-type=application/json,application/xml".
So I understand that you are specifying the type, but why? Under what conditions would it matter. For example, if I have the following method, and I comment out the #Produces annotation, it still returns JSON.
#GET
#Path("/json")
//#Produces({MediaType.APPLICATION_JSON})
public String getJson(){
return toJson(getResults());
}
The API doc says 'If not specified then a container will assume that any type can be produced.' So why would I not want the container to assume that?
I think it depends on your JAX-RS implementation but here's Jersey's explanation of their #Produces annotation: https://jersey.java.net/documentation/latest/jaxrs-resources.html#d0e1809
Basically, it's up to the client to determine what content type the server should spit back.
If the client supports more than one content type, you can sometimes specify the priority of content types to return, for a given method:
#Produces({"application/xml; qs=0.9", "application/json"})
In the above sample, if client accepts both "application/xml" and "application/json" (equally), then a server always sends "application/json", since "application/xml" has a lower quality factor.
If a client requests your JSON only resource with an Accept: application/xml; header then strictly speaking the server should return a 406 (not acceptable) status code, not the JSON string.
If you use the #Provides annotation, the container should handle this case for you and that is why you should use it.
I've a method called createCustomer(),it is a POST method and it consumes both MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, now I want to check the actual MIME type of the request that came from client, it can be XML or JSON and based on the request's MIME type I want to call two different methods.
Can you please provide me the code to check the MIME type of the Incoming request and based on the type call two different methods.
The sample code looks like below:
#POST
#Path("/createCustomer")
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response createCustomer(Customer customer ) {
//if the request is in JSON then call the method createCustomerJSON()
//else if the request is in XML then call the method createCustomerXML()
//String output = output from either the method createCustomerJSON() or createCustomerXML()
return Response.status(200).entity(output).build();
}
First of all, it would have been great to have posted some code.
Secondly, one solution would be to create two methods with the same path, one consuming XML and one consuming JSON.
#POST
#Path("yourPath")
#Consumes(MediaType.APPLICATION_XML);
public Response createCustomerXML() {...}
#POST
#Path("yourPath")
#Consumes(MediaType.APPLICATION_JSON);
public Response createCustomerJSON() {...}
javax.ws.rs.Consumes annotation is probably what you need. By putting different annotations on different methods you can split handling of XML and JSON.
From javadoc:
Defines the media types that the methods of a resource class or MessageBodyReader can accept. If not specified, a container will assume that any media type is acceptable. Method level annotations override a class level annotation. A container is responsible for ensuring that the method invoked is capable of consuming the media type of the HTTP request entity body. If no such method is available the container must respond with a HTTP "415 Unsupported Media Type" as specified by RFC 2616.
I am currently working on a project which was maintained by some other team and now i need to maintain it. While i was going through the project i found some thing below:
In jax-rs controller it was annotated by #Consumes(MediaType.APPLICATION_JSON) but the method takes request body as String rather than JSON. Then what is the use of the annotation? Does it help in content negotiation anyway?
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createCake(final String requestBody){.......}
How it is converting a JSON body to string?
My technology stack if it anyway helps to answer:
JAX-RS
Spring 3.2
Jersey 2.4
The #Consumes serves the following purpose. It restricts the mapping for your handlers. For example, you may have two handlers for the path /resource, one mapped to consume XML and the other mapped to consume json. The dispatcher will choose the right one based on the request's content-type.
The parameter type can be anything as long as there is an appropriate converter for the specified media type to the parameter type itself. In this case, there's very likely a converter from any media type to String.
Currently I have registered a Gson Provider which correctly is
used whenever my request is consuming or producing json.
The problem is that I have a request that needs the Post data as
either a byte[], InputStream, Reader, or String.
The reason I need the "raw" data is that I have some third party code where
it expects to do its own deserialization.
No matter which of these four types I specify my Post method to expect,
the GsonReader will complain and rightly so.
Expected a string but was BEGIN_OBJECT
Depending on the type there is a different error, but it all boils down to the
fact that I don't want this Provider/MessageBodyReader to run.
Also, I don't have control of the Accept and Content-type headers of the Posted data.
They will be application/json.
You can "modify" the accept/content-type headers of a request in a filter. So, if there is any way you can recognize that for this request, you don't want to use GSON, you can write a ContanerRequestFilter that modifies the headers.
If using GSON provider depends on a method the request gets matched to, you can implement ResourceFilterFactory that applies (returns) the ContainerRequestFilter (that modifies the content-type header to something other than json) just for the applicable methods (you can even introduce a custom annotation, annotate such methods with it and in the resourcefilterfactory return the containerrequestfilter only if the method passed to it is annotated with that annotation).
Here are the relevant links:
ContainerRequestFilter javadoc
ResourceFilterFactory javadoc
RolesAllowedResourceFilterFactory - you can use this as an example of a resource filter factory implementation