Disable Jersey Provider per request - java

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

Related

Spring 3.1 or Later #RequestMapping Consumes/Produces

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".

Process REST request based on the type of MIME Type

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.

#Produces annotation in JAX-RS

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.

Spring: How to use #RequestBody after reading inputStream

I'm having the issue described here but it's not clear to me how to address it.
The issue is that I have an AuthenticationProvider which has to read the body of the request. When my controller method wants to use that data via #RequestMapping, however, it's empty because the Provider has already read the inputStream.
Is there a way to obtain an inputReader from Request which supports mark/reset so my provider can simply roll the stream back to it's initial state after it does the authentication? It seems crazy that the default behavior of the filter is destructive modification of the request object.
The Provider should be triggered only in specific cases, so it shouldn't affect your whole application. But if you need the body in the requests handled by the provider, then you still have a workaround:
implement a servlet Filter
wrap the request
in the wrapper cache the request body, and then override the getInputStream() method to return a ByteArrayInputStream with the cached request body. That way it can be read muiltiple times.
spring's AbstractRequestLoggingFilter does something similar and has an example wrapper, you can check it.

Easy REST resource versioning in JAX-RS based implementations?

Best practice for REST resource versioning is putting version information into Accept/Content-Type headers of HTTP request leaving URI intact.
Here is the sample request/response to REST API for retrieving system information:
==>
GET /api/system-info HTTP/1.1
Accept: application/vnd.COMPANY.systeminfo-v1+json
<==
HTTP/1.1 200 OK
Content-Type: application/vnd.COMPANY.systeminfo-v1+json
{
“session-count”: 19
}
Pay attention that version is specified in MIME type.
Here is another request/response for version 2:
==>
GET /api/system-info HTTP/1.1
Accept: application/vnd.COMPANY.systeminfo-v2+json
<==
HTTP/1.1 200 OK
Content-Type: application/vnd.COMPANY.systeminfo-v2+json
{
“uptime”: 234564300,
“session-count”: 19
}
See http://barelyenough.org/blog/tag/rest-versioning/ for more explanation and examples.
Is it possible to implement this approach easily in Java-targeted JAX-RS based implementations, such as Jersey or Apache CXF?
The goal is to have several #Resource classes with the same #Path value, but serving the request based on actual version specified in MIME type?
I've looked into JAX-RS in general and Jersey in particlaur and found no support for that. Jersey doesn't give a chance to register two resources with the same path. Replacement for WebApplicationImpl class needs to implemented to support that.
Can you suggest something?
NOTE: It is required for multiple versions of the same resource needs to be available simultaneously. New versions may introduce incompatibale changes.
JAX-RS dispatches to methods annotated with #Produces via the Accept header. So, if you want JAX-RS to do your dispatching, you'll need to leverage this mechanism. Without any extra work, you would have to create a method (and Provider) for every media type you wish to support.
There's nothing stopping you from having several methods based on media type that all call a common method to do that work, but you'd have to update that and add code every time you added a new media type.
One idea is to add a filter that "normalizes" your Accept header specifically for dispatch. That is, perhaps, taking your:
Accept: application/vnd.COMPANY.systeminfo-v1+json
And converting that to, simply:
Accept: application/vnd.COMPANY.systeminfo+json
At the same time, you extract the version information for later use (perhaps in the request, or some other ad hoc mechanism).
Then, JAX-RS will dispatch to the single method that handles "application/vnd.COMPANY.systeminfo+json".
THAT method then takes the "out of band" versioning information to handle details in processing (such as selecting the proper class to load via OSGi).
Next, you then create a Provider with an appropriate MessageBodyWriter. The provider will be selected by JAX-RS for the application/vnd.COMPANY.systeminfo+json media type. It will be up to your MBW to figure out the actual media type (based again on that version information) and to create the proper output format (again, perhaps dispatching to the correct OSGi loaded class).
I don't know if an MBW can overwrite the Content-Type header or not. If not, then you can delegate the earlier filter to rewrite that part for you on the way out.
It's a little convoluted, but if you want to leverage JAX-RS dispatch, and not create methods for every version of your media type, then this is a possible path to do that.
Edit in response to comment:
Yea, essentially, you want JAX-RS to dispatch to the proper class based on both Path and Accept type. It is unlikely that JAX-RS will do this out of the box, as it's a bit of an edge case. I have not looked at any of the JAX-RS implementations, but you may be able to do what you want by tweaking one of the at the infrastructure level.
Possibly another less invasive option is to use an age old trick from the Apache world, and simply create a filter that rewrites your path based on the Accept header.
So, when the system gets:
GET /resource
Accept: application/vnd.COMPANY.systeminfo-v1+json
You rewrite it to:
GET /resource-v1
Accept: application/vnd.COMPANY.systeminfo-v1+json
Then, in your JAX-RS class:
#Path("resource-v1")
#Produces("application/vnd.COMPANY.systeminfo-v1+json")
public class ResourceV1 {
...
}
So, your clients get the correct view, but your classes get dispatched properly by JAX-RS. The only other issue is that your classes, if they look, will see the modified Path, not the original path (but your filter can stuff that in the request as a reference if you like).
It's not ideal, but it's (mostly) free.
This is an existing filter that might do what you want to do, if not it perhaps can act as an inspiration for you to do it yourself.
With current version of Jersey, I would suggest an implementation with two different API methods and two different return values that are automatically serialised to the applicable MIME type. Once the requests to the different versions of the API are received, common code can be used underneath.
Example:
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public VersionOneDTO get(#PathParam("id") final String id) {
return new VersionOneDTO( ... );
}
#GET
#Path("/{id}")
#Produces("application/vnd.COMPANY.systeminfo-v2+json;qs=0.9")
public VersionTwoDTO get_v2(#PathParam("id") final String id) {
return new VersionTwoDTO( ... );
}
If method get(...) and get_v2(...) use common logic, I would suggest to put that in a common private method if it's API related (such as session or JWT handling) or else in a common public method of a Service Layer that you access via inheritance or Dependency Injection. By having two different methods with different return types, you ensure that the structure returned is of correct type for the different versions of the API.
Note that some old client may not specify Accept header at all. That means implicitly that they would accept any content type, thus any version of your API. In practice, this is most often not the truth. For this reason you should specify a weight to newer versions of the API using the qs extension of the MIME type as shown in the #Produces annotation in the example above.
If you are testing with restAssured it would look something like this:
import static com.jayway.restassured.RestAssured.get;
import static com.jayway.restassured.RestAssured.given;
#Test
public void testGetEntityV1() {
given()
.header("Accept", MediaType.APPLICATION_JSON)
.when()
.get("/basepath/1")
.then()
.assertThat()
... // Some check that Version 1 was called
;
}
#Test
public void testGetEntityV1OldClientNoAcceptHeader() {
get("/basepath/1")
.then()
.assertThat()
... // Some check that Version 1 was called
;
}
#Test
public void testGetEntityV2() {
given()
.header("Accept", "application/vnd.COMPANY.systeminfo-v2+json")
.when()
.get("/basepath/1")
.then()
.assertThat()
... // Some check that Version 2 was called
;
}
One possible solution is to use one #Path with
Content-Type:
application/vnd.COMPANY.systeminfo-{version}+json
Then, inside the method of the given #Path you can call the version of the WebService
If you're using CXF, you could use the technique specified here to build a new serialization provider (building off the existing infrastructure) which produces the data in the specific format desired. Declare a couple of those, one for each specific format that you want, and use the #Produces annotation to let the machinery handle the rest of the negotiation for you, though it might also be an idea to support the standard JSON content type too so that normal clients can handle it without needing to grok your specialness. The only real question then becomes what is the best way to do the serialization; I presume you can figure that out for yourself…
[EDIT]: Further digging in the CXF documentation leads to the revelation that both the #Consumes and #Produces annotations are considered to be axes for doing selection. If you want to have two methods that handle the production of the response for different media types, you most certainly can. (You'll have to add the serialization and/or deserialization providers if you're using custom types, but you can do the delegation of the majority of the work to the standard providers.) I'd still like to caution that you should still ensure that the resource indicated by the path should be the same in both cases; to do otherwise is not RESTful.
You should be able to use different classes with the same path provided they consume/produce different media types. So this should work with any jax-rs provider:
#Path("/api/system-info")
#Consumes("application/vnd.COMPANY.systeminfo-v1+json")
#Produces("application/vnd.COMPANY.systeminfo-v1+json")
public class SystemInfoResourceV1 {
}
and
#Path("/api/system-info")
#Consumes("application/vnd.COMPANY.systeminfo-v2+json")
#Produces("application/vnd.COMPANY.systeminfo-v2+json")
public class SystemInfoResourceV2 {
}

Categories

Resources