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.
Related
I've got super specific logic of mapping content of the body to bean. I tried to use ParamConverterProvider but it works only for #PathParam, #QueryParam and etc.
Is there simple way of customizing mapping body to bean?
In jersey it uses provided reader types to convert from the request body to arbitrary types. You'll want to implement MessageBodyReader with your bean.
Be sure to annotate your reader with #Provided and make sure it's explicitly registered in your ApplicationConfig or in a package that will be automatically scanned.
You shouldn't annotate your method that takes in the bean:
#Get
...
public Response doGet(MyBeanType bean) {
...
}
another (dirty!) Approach could be to accept an Object and transform the object into your needed Pojo-Structure.
This of course depends of the structure which is handed in. Just debug to find out how your object looks like.
#Get
public Response doSome(Object o){
if (o instanceof List) { .... }
}
Anyway, this should better be done with a MessageBodyReader as John H suggested!
Thanks to #RestController I don't need to add annotation #ResposneBody, cause spring knows that it is rest controller, and he will not generate view, but instead it will return json object.
Unfortunately there is one more annotation related to this topic. It is #RequestBody, when controller method accept json object as a parameter. And it will have to be pointed before that parameter.
My question is there a way to get rid of that annotation (#RequestBody).? If my controller is rest controller (#RestController instead of regular #Controller) it should be demanded from spring?
No, you'll have to specify #RequestBody. A Java method can have only a single return value, and so the #ResponseBody is unambiguous, but there are multiple possible ways that mapped controller parameters might be interpreted (in particular, using #ModelAttribute with form encoding is a very common alternative to #RequestBody with JSON), and you'll need to tell Spring how to map the incoming request.
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.
I am currently developing a REST webservice using Jersey and Guice as a DI-container.
For handling the requests I am relying on a GuiceServletContextListener which is configured similar to the following:
bind(UserResource.class);
//Some other root-level resources for REST
serve("/rest/*").with(GuiceContainer.class);
As I have to deal with hierarchical data (One user should have their own items and it should be possible to access items of other users in the form of /rest/user/[Username]/item). For this, I am using Jersey's support for subresources.
For example, my UserResource contains the following method (ItemResource.Factory is a factory interface whose implementation is automatically provided by Guice's FactoryModuleBuilder):
#Inject
private ItemResource.Factory _itemResourceFactory;
#Path("/{username}/item")
public ItemResource getItems(#PathParam("username") String username) {
User user = //...
return this._itemResourceFactory.create(user);
}
ItemResource (the subresource) then again is implemented as a normal Jersey class based on the User passed in in the constructor.
However, my subresources need access to #Context fields (like UriInfo or HttpServletRequest), too. According to the Jersey documentation, #Context fields are not injected for subresources as their lifecycle is unknown (and the documentation seems to be true).
This is very unfortuante for me: I really need access to those values.
As a workaround, I am currently passing those values as additional constructor parameters to my subresources which I perceive as everything but comfortable.
Is there any possibility to tell Jersey to inject them anyway?
Nevertheless, even better would be if Guice itself was able to inject the #Context fields.
Simply swapping the #Context for #Inject, however, doesn't work as Guice has no registrations for types like UriInfo or HttpServletRequest.
Can I somehow create those mappings?
The problem is, that I don't know how to access the request specific values inside a Guice Provider implementation.
Are there maybe any helper methods to get access to the current instances of those Jersey objects so I can write the necessary providers?
Or are those implementations maybe already available somewhere out there?
I am not sure I understood your problem. Can you post the code related to "passing those values as additional constructor parameters"?
You can inject the Context like this:
#Path("/{username}/item")
public ItemResource getItems(#Context HttpServletRequest request, #PathParam("username") String username) {
Maybe you could inject fields programmatically? Guice provides this through the Injector class:
Injector injector = Guice.createInjector(...);
injector.injectMembers(someObjectToInject);
See http://code.google.com/p/google-guice/wiki/Injections for more information on this topic.
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.