Passing the URI Path to the JAX-RS Providers - java

I recently implemented a Jersey JAX-RS Rest service. I created a JIBX provider that allows one to unmarshal and marshall between XML and Java types. I would like to also version my service by specifying the version in the URL path. Versioning would include the version of message binding used to marshal and unmarshall the Java types.
Therefore, it is necessary that the version is passed to the custom JIBX provider, and therefore the URL path that contains the version. However, the Provider interfaces (MessageBodyWriter and MessageBodyReader) do not provide the URI path in their interface methods.
The following is the method signature of the writeTo() method of the MessageBodyWriter interface:
writeTo(Object, Type, Annotation[], MediaType, MultivaluedMap, OutputStream)
This method parameters does not contain the path uri, therefore, the custom jibx provider cannot know which message binding version it should use to marshall the Java type. Is there a way around this?

If you want something a bit more JAX-RS specific than HttpServletRequest, you can inject a javax.ws.rs.core.UriInfo.
public class MyProvider implements MessageBodyWriter {
#javax.ws.rs.core.Context
javax.ws.rs.core.UriInfo uriInfo;
}
I'm assuming that you're using a #javax.ws.rs.PathParam to capture the path parameter. You can then potentially use UriInfo.getPathParameters(). You can also fall back to UriInfo.getPathSegments() to get the information you're looking for. This saves you the trouble of parsing the request URI yourself. Any JAX-RS implementation should be able to do this.

You can access the URI path from the Provider by defining the #Context annotation on a field on the Provider.
For example,
public class CustomProvider implements MessageBodyWriter
{
#Context HttpServletRequest request;
....
}
This field will automatically be set for each request. Even though the request is set as a field, the value is thread-safe as the actual request is using a proxy and most likely thread local to determine the request that belongs to thread.

Related

#Consumes(MediaType.APPLICATION_JSON) annotation but getting request body as string

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.

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

Can JAX-RS construct an object using only #*Param annotations?

Is it possible to get a JAX-RS implementation such as RESTEasy to automatically construct an object containing only #*Param annotations, such as #MatrixParam? I have the following class representing pagination:
public class Pagination {
#MatrixParam("after") public String afterKey;
#MatrixParam("from") public String fromKey;
#MatrixParam("to") public String toKey;
#MatrixParam("before") public String beforeKey;
#MatrixParam("count") public int count;
}
I'd like to pass it to JAX-RS methods such as this:
#GET
#Produces("text/html")
Response asHtml(Pagination pagination);
I was hoping RESTEasy would call the default constructor and then inject the field values, but I get a "Could not find message body reader" error. Obviously, there is no message body, and adding a dummy String constructor did not help. Do I need to create my own #Provider for this? If so, could such a thing be made generic and leverage the built-in #*Param injection features?
I know this a bit old subject, but JAX-RS 2.0 has #BeanParam annotation that does this.
#GET
#Produces("text/html")
Response asHtml(#BeanParam Pagination pagination);
From an answer to a similar question, there's a RESTEasy-specific annotation, #Form, that allows this:
This is a RESTEasy specific annotation that allows you to re-use any #*Param annotation within an injected class. RESTEasy will instantiate the class and inject values into any annotated #*Param or #Context property. This is useful if you have a lot of parameters on your method and you want to condense them into a value object.
The #Form annotation goes on the resource method parameter or resource field, not the value object class:
#GET
#Produces("text/html")
Response asHtml(#Form Pagination pagination);
Other implementations may require an entity provider. From the JAX-RS spec (JSR-339):
3.3.2.1 Entity Parameters
The value of a parameter not annotated with #FormParam or any of the annotations listed in Section 3.2, called the entity parameter, is mapped from the request entity body. Conversion between an entity body and a Java type is the responsibility of an entity provider, see Section 4.2. Resource methods MUST have at most one entity parameter.

What objects can I inject using the #Context annotation?

I'm new to JAX-RS and I'm trying to understand how the #Context annotation is supposed to work.
At the javadoc there is a list of six classes (Application, UriInfo, Request, HttpHeaders, SecurityContext, Providers). However I find code on the web that use the this annotation with other types, for example:
#GET
public String something(#Context HttpServletRequest req) {
}
Is there a list of supported types that can be used with this annotations? Does this list change between implementation of the standard?
I'm currently experimenting with Jersey and I'm worried that I'll write code that cannot be ported to other JAX-RS implementation.
The #Context annotation allows you to inject request/response context details into JAX-RS provider and resource classes. Injection can be performed into a class field, a bean property or a method parameter.
The following list summarizes all the types that can be injected using the #Context annotation, according to the JAX-RS 2.0 specification:
javax.ws.rs.core.Application
javax.ws.rs.core.HttpHeaders
javax.ws.rs.core.Request
javax.ws.rs.core.SecurityContext
javax.ws.rs.core.UriInfo
javax.ws.rs.core.Configuration
javax.ws.rs.container.ResourceContext
javax.ws.rs.ext.Providers
Except for Configuration and Providers, which
are injectable in both client and server-side providers, all the other types are server-side only.
The following types are available only when the application is deployed in a servlet container:
javax.servlet.HttpServletRequest
javax.servlet.HttpServletResponse
javax.servlet.ServletConfig
javax.servlet.ServletContext
JAX-RS 2.1 introduced other types that can be injected with #Context:
javax.ws.rs.sse.Sse
javax.ws.rs.sse.SseEventSink
Besides the standard types listed above, JAX-RS implementations, such as Jersey, RESTEasy and Apache CXF, might define their own types that can be injected using #Context.
Find below a quick description of each JAX-RS type available for injection:
Application: The instance of the application-supplied Application subclass can be injected into a class field or method parameter. Access to the Application subclass instance allows configuration information to be centralized in that class.
URIs and URI templates: UriInfo provides both static and dynamic, per-request information, about the components of a request URI.
Headers: HttpHeaders provides access to request header information either in map form or via strongly typed convenience methods. Response headers may be provided using the Response class.
Content negotiation and preconditions: The methods of Request allow a caller to determine the best matching representation variant and to evaluate whether the current state of the resource matches any preconditions in the request.
Security context: The SecurityContext interface provides access to information about the security context of the current request. The methods of SecurityContext provide access to the current user principal, information about roles assumed by the requester, whether the request arrived over a secure channel and the authentication scheme used.
Providers: The Providers interface allows for lookup of provider instances based on a set of search criteria. This interface is expected to be primarily of interest to provider authors wishing to use other providers functionality. It is injectable in both client and server providers.
Resource context: The ResourceContext interface provides access to instantiation and initialization of resource or subresource classes in the default per-request scope. It can be injected to help with creation and initialization, or just initialization, of instances created by an application.
Configuration: Both the client and the server runtime Configurations are available for injection in providers (client or server) and resource classes (server only).
SSE events: SseEventSink represents the incoming SSE connection and provides methods to send events. Sse provides factory methods for events and broadcasters.
This post written by Arjan Tijms suggests that future versions of JAX-RS may have a stronger integration with CDI. So #Context may be deprecated and then removed in favor of #Inject:
JAX-RS 2.2
For some reason, one that has largely been lost in time, JAX-RS uses its own dependency injection system based on #Context instead of CDI's #Inject. While JAX-RS was updated at the last moment before its initial release to have some level of support for CDI, the fact that JAX-RS resources are not CDI beans has unnecessarily hold back the spec and has caused confusion even since JAX-RS was introduced in EE 6 (2009).
This changeover to CDI could possibly happen in 2 steps; in JAX-RS 2.2 everything that can now be injected by #Context should also be injectable using #Inject and JAX-RS resources would be CDI beans by default (perhaps unless explicitly disabled). At the same time #Context would be deprecated. In JAX-RS 3.0 #Context would then be actually removed.
The riveting JAX-RS specification defines all the standard types you can inject via #Context.
But if I were you, I would just consult the specific documentation of your chosen provider to see what is available.
For example, RESTEasy provides these values via #Context. Meanwhile, Jersey provides these. Obviously there will be overlap because of the standard context values.
The #Context annotation can be used to inject 12 objects. Here is a quick summary of each of them
HttpHeaders - HTTP header valuesand parameters
UriInfo - URI query parameters and path variables
SecurityContext - Gives access to security-related data for the given HTTP request
Request - Allows precondition request processing
ServletConfig - The ServletConfig
ServletContext - The ServletContext
HttpServletRequest - The HttpServletRequest instance for the request
HttpServletResponse - The HttpServletResponse instance
Application, Configuration, and Providers -> Provide information about the JAX-RS application, configuration and providers
ResourceContext - Gives access to resource class instances
All of these instances can be injected in the resource method
#Path("/")
public class EndpointResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllHttpHeaders(final #Context HttpHeaders httpHeaders){
// Code here that uses httpHeaders
}
}
or as a field:
#Path("/")
public class EndpointResource {
private final #Context HttpHeaders httpHeaders;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllHttpHeaders(){
// Code here that uses httpHeaders
}
}
Here is a five part series answering the question What is #Conext used for?
To extend already provided answers: JAX-RS implementations usually provide a way to access the context. For RESTEasy it's ResteasyContext. The context can be listed by
ResteasyContext.getContextDataMap()
Keys of returned map can look like:
interface javax.ws.rs.core.Request
interface javax.ws.rs.core.HttpHeaders
interface javax.ws.rs.ext.Providers
class org.jboss.resteasy.plugins.server.Cleanables
class org.jboss.resteasy.core.PostResourceMethodInvokers
class org.jboss.resteasy.core.InternalDispatcher
interface javax.ws.rs.container.ResourceInfo
interface io.vertx.core.http.HttpServerResponse
interface io.vertx.core.Context
interface org.jboss.resteasy.spi.Registry
interface org.jboss.resteasy.spi.HttpRequest
interface org.jboss.resteasy.spi.ResteasyDeployment
interface javax.ws.rs.container.ResourceContext
interface org.jboss.resteasy.spi.Dispatcher
interface io.vertx.ext.web.RoutingContext
interface io.vertx.core.http.HttpServerRequest
interface io.vertx.core.Vertx
interface javax.ws.rs.core.Configurable
interface org.jboss.resteasy.spi.ResteasyAsynchronousContext
interface javax.ws.rs.core.SecurityContext
interface javax.ws.rs.core.Configuration
interface javax.ws.rs.core.UriInfo
interface org.jboss.resteasy.spi.HttpResponse

Disable Jersey Provider per request

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

Categories

Resources