How to inject dynamic Bean parameter into Jersey method? - java

I'm developing a webservice with Jersey 2.0 and Spring.
I want to be able to inject a bean into my methods. Bean parameters can be obtained using #BeanParam. However, I need a 'dynamic' bean injected. I need this bean to contain all of the query parameters passed to the method.
For example, if I make a request GET /posts?title=lorem&date=2011-01-01&tag=game
And I have a method like
#Path('/posts')
public class PostService{
#GET
public Response getAll(#QueryParam("page") int page,
#QueryParam("pageSize") int pageSize,
#BeanParam SearchParameters sp){
sp.getTitle();
sp.getDate();
sp.getTag();
}
}
I might be easier to get a map of query parameters Map<String, String>.

#BeanParam allows to put more injectable parameters into one bean (POJO), so that you do not have so many injectable parameters in the resource method, in resource method constructor or so many injectable fields in the resource class. You can encapsulate them into bean injected with #BeanParam. This deals with parameters like #HeaderParam, #QueryParam and such. But you can also inject ContainerRequestContext, UriInfo, SecurityContext or any other injectable object into your bean.
However, in your case you need to use the map of query parameters because you need all parameters and not only specific parameters known before. In order to get them, you can inject UriInfo and get query parameters from it:
#GET
public Response get(#Context UriInfo uriInfo) {
MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
String myParam = queryParameters.getFirst("myParam");
...
}
Or you can use #BeanParam and inject #UriInfo into a bean.

Related

#QueryParam or HttpServletRequest in java

I have resource class - UserResource interface and I have defined an endpoint as getUsers.
Now I want to filter these users based on users status - (Active, Inactive) and its not mandatory so if I does not pass the status it will fetch all the users.
Now the question is should I pass this as #QueryParam or get it from
HttpServletRequest - httpServletRequest.getParameter("Status").
Which one from the above two is best way and in what scenario I should use which one.
1. First way is pass the status as query param and define in the resource file itself. Here UserResource is the controller interface or resource class. In getUsers method has #QueryParam.
import javax.ws.rs.core.Response;
#Path(/user)
public interface UserResource{
#GET
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
Response getUsers(#QueryParam("status") String status);
}
#Component
Public class UsersResourceImpl implement UserResource{
public Response getPlan(String status){
String userStatus = status;
// some logic
}
}
2. Second way is get the query param from HttpServletRequest. so I have
autowired the HttpServletRequest and getting the query param from the
httpservletrequest.
import javax.ws.rs.core.Response;
#Path(/user)
public interface UserResource {
#GET
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
Response getUsers();
}
import javax.servlet.http.HttpServletRequest;
#Component
Public class UsersResourceImpl implements UserResource{
#Autowired
private HttpServletRequest httpRequest;
public Response getPlan(String status){
String status = httpRequest.getParameter(status)
// some logic
}
}
'''
Well, I honestly don't see any appealing reason to avoid using the #QueryParam annotation given that you need the value from a query parameter.
Some benefits of using #QueryParam that I can think of:
The #QueryParam annotation will automatically bind the value(s) of a query parameter to a resource method parameter, resource class field, or resource class bean property. So you won't need to extract and parse parameters manually, once you respect some rules described in the documentation:
The type T of the annotated parameter, field or property must either:
Be a primitive type
Have a constructor that accepts a single String argument
Have a static method named valueOf or fromString that accepts a single
String argument (see, for example, Integer.valueOf(String))
Have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.
Be List<T>, Set<T> or SortedSet<T>, where T satisfies 2, 3 or 4 above. The resulting collection is read-only.
#QueryParam can be combined with #DefaultValue to define a default value for the parameter in case it's not present in the request.
If multiple endpoints support the same query parameters, you could aggregate them in a class and receive an instance of such class as a #BeanParameter.
Go with annotations (i.e #QueryParam) that's why we chose such framework , remember convention over configuration.

ParamConverter based on HTTP header value

I am creating a Jersey application using Jersey version 2.27.
One of my resources is using #QueryParam to read a query parameter value.
I am creating a ParamConverter and ParamConverterProvider such that I can specify the type of the parameter:
#GET
public MyObject getObject(#QueryParam("myvalue") MyParamObject param) {
}
My issue is that the parsing of the query parameter depends on the value of an HTTP header.
How do I access the HTTP headers of the request inside a ParamConverter?
I have tried injecting the ContainerRequestContext inside the ParamConverterProvider, but this results in the following error:
java.lang.IllegalStateException: Not inside a request scope.
I managed to solve it by injecting a ServiceLocator inside the ParamConverterProvider and pass it to the ParamConverter. The ParamConverter can then fetch the ContainerRequestContext in the fromString() method:
public Message fromString(final String value) {
final ContainerRequestContext requestContext = injector.getService(ContainerRequestContext.class);
}

Jersey: Is there a way in a bean to put #QueryParam and #FormParam on same bean?

I am creating a REST API and in one endpoint properties are supposed to come as QUERY parameters and in another request same properties are supposed to come either as headers or FORM parameters. Is there a way to define #QueryParam and #FormParam in same field inside a bean which I will use in Resource method as #BeanParam
I don't think its possible. You can use the following:
public void foo (#QueryParam("bar") String bar1, #FormParam("bar") String bar2) {
String bar = isEmpty (bar1) ? bar2 : bar1;
}

Ambiguous mapping when using RequestBody annotation

I am trying to create an endpoint in Spring Boot that accepts both single object or an array of these objects. I know that mappings need to have unique signiture so I am wondering what is the correct way to make it work using POJOs?
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<String> postSingleFoo(HttpServletRequest request,
#RequestBody(required = true) Foo foo) {
// process
}
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<String> postMultiFoo(HttpServletRequest request,
#RequestBody(required = true) Foo[] foo) {
// process
}
Obviously I am getting an exception for ambiguous mapping. But I would still like to use POJOs in my #RequestBody annotation since i am performing couple of conversions in them.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'fooController' method
public void com.usquared.icecream.lrs.controller.FooController.postSingleFoo(javax.servlet.http.HttpServletRequest,java.lang.String)
to {[/foo],methods=[POST]}: There is already 'fooController' bean method
What is the recommended approach to implement such feature correctly?
This is not a problem that can be fixed through Spring MVC. Spring MVC creates mappings from the #RequestMapping annotating your handler methods. These help distinguish how Spring MVC delegates HTTP requests to be handled by your methods. Your current configuration attempts to map two handler methods to the same request details. That can never work.
One solution, assuming you're expecting JSON and working with Jackson, is to configure your ObjectMapper to accept single values as arrays and define a single handler method with an array parameter. For example, you'd keep only this handler method
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<String> postMultiFoo(HttpServletRequest request,
#RequestBody(required = true) Foo[] foo) {
// process
}
but configure your ObjectMapper as such
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
The configuration depends on how your application is configured. With Spring Boot, it should be as simple as declaring a #Bean method for ObjectMapper. With your typical Spring MVC application, you'll need to register a MappingJackson2HttpMessageConverter with a custom ObjectMapper.
If your JSON, the request body, contained
{
"someProperty":"whatever"
}
Jackson would be able to wrap the single value into a Foo[] and Spring MVC would pass that as an argument to your handler method. You can then check the length of the array and act accordingly.

Jersey 1.x with Jackson : Customising the response JSON

I am using Jersey 1.19 to implement a rest api and Jackson to provide JSON support. My resource entities are deeply nested and I want to flatten them out before sending them over. I also want to provide support for filtering based on query params. Example GET /users/1234 returns the whole user resource while GET /users/1234?filter=username,email will return the user resource with only the given fields included.
The approach I have currently taken is a subclass of JsonSerializer which flattens the hierarchy, but cannot handle parameter based filtering as it is independent of the request/response cycle. Google search pointed me to MessageBodyWriter. Looks like what I need but the writeTo method which handles the serializing doesn't take any parameter that would let me access the request, and hence the query params. So I am confused how to access those params in this method.
Any ideas are welcome
So I am confused how to access those params in this method.
You can inject UriInfo with #Context into the MessageBodyWriter. Then call uriInfo.getQueryParameter() to get the params. For example
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class YourWriter implements MessageBodyWriter<Something> {
#Context UriInfo uriInfo;
...
#Override
public void writeTo(Something t, Class<?> type, Type type1, Annotation[] antns,
MediaType mt, MultivaluedMap<String, Object> mm, OutputStream out)
throws IOException, WebApplicationException {
String filter = uriInfo.getQueryParameters().getFirst("filter");
}
}
Another option is to use a ContextResolver and use preconfigured ObjectMappers for different scenarios. You can also inject the UriInfo into the ContextResolver. For example
You should be able to pass a list in and/or you can expose the Request object if you want to go that route.
Try ...
#Context
UriInfo uriInfo;
#Context
HttpServletRequest request;
or try altering your Rest method to something like...
#GET
#Path("/myMethodLocator")
#Consumes(MediaType.APPLICATION_JSON)
...
public <whatever type you are returning> myMethod(List<String> filterByList) ...
...

Categories

Resources