Jersey 1.x with Jackson : Customising the response JSON - java

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

Related

Understanding REST APIs - What are Context and #Context?

I recently went through restful web services tutorial, but couldn't understand what a context is. Can someone explain what it it and also what #Context does?
JAX-RS provides the #Context annotation to inject 12 object instances related to the context of the HTTP request and they are:
SecurityContext - Security context instance for the current HTTP request
Request - Used for setting precondition request processing
Application, Configuration, and Providers -> Provide access to the JAX-RS application, configuration, and providers instances
ResourceContext - Resource contect aclass instances
ServletConfig - The ServletConfig instance instance
ServletContext - The ServletContext instance
HttpServletRequest - The HttpServletRequest instance for the current request
HttpServletResponse - The HttpServletResponse instance for the current request
HttpHeaders - Maintains the HTTP header keys and values
UriInfo - Query parameters and path variables from the URI called
It is a little confusing to have both an #Inject (or #Autowired in Spring) and #Context that does the same job, but it is hoped to bring more alignment to Java EE in the next edition. In the meantime, you will have to make do.
An interesting feature is that all of these instances can be injected as a field value or directly into the resource method.
An example of injection into the resource method parameter list:
#Path("/")
public class EndpointResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllHttpHeaders(final #Context HttpHeaders httpHeaders){
// Code here that uses httpHeaders
}
}
An example of injection into a field:
#Path("/")
public class EndpointResource {
private final #Context HttpHeaders httpHeaders;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllHttpHeaders(){
// Code here that uses httpHeaders
}
}
If you want to know more, take a look at this series of articles answering the question What is #Conext in JAX-RS used for?
For an explanation about context in programming terms, have a look at this answer.
The JAX-RS API provides a #Context annotation. In general, such annotation can be used to obtain contextual Java types related to the request or response. Those types can be injected into classes managed by the JAX-RS runtime.
For example, to inject the HttpServletRequest in your resource method, you can do the following:
#GET
public Resonse foo(#Context HttpServletRequest request) {
...
}
Additional resources:
Types that can be injected with #Context
Jersey documentation about resources
context is a react Hook feature that helps to pass the data from one component to another without calling the props at each level ... it avoids prop drilling. by defining the provider in one context component and then you can call everywhere and every time when you need.
REST is an architectural style and one of the way to implement web-services. (Other is SOAP). There are many implementations of REST architecture and one of them in java is Jersey (https://jersey.java.net/)
#context is annotation in Jersey framework. It's a class from jax rs jar. (https://jersey.java.net/apidocs-javax.jax-rs/2.0.1/javax/ws/rs/core/Context.html)

Get JSON message in JAX-RS web service filter

I have a web service method as follow (deployed on WebLogic 12.2.1), which I can receive the JSON request body in the POJO object "requestParameters":
#POST
#SessionChecker
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("LogIn")
public Response logIn(#Context HttpServletRequest request, Parameters requestParameters) {
....
}
I have a filter that I want to intercept the request before the above web service method is called.
#Provider
#SessionChecker
public class CheckSessionFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest servletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws WebApplicationException {
....
}
}
In the filter() method, how do I get the JSON message body into the POJO object of type Parameters? I just need to get one attribute from the JSON message. After the filter is done, the JSON message should pass on to the web service method without change.
Thanks in advance.
Here's the problem. When your filter is hit, the request stream (InputStream) hasn't been read yet. So if you try to read it, then Jersey will not be able to read it, as a stream can only be read once, so it will be empty.
Jersey actually offers a solution to this. The ContainerRequestContext, is actually an instance of Jersey specific ContainerRequest. If you look at the linked API, you will find a bufferEntity() method. This allows us to read the entity, and Jersey will be able to read it again. So your first step is to make that call
#Override
public void filter(ContainerRequestContext requestContext)
ContainerRequest cr = (ContainerRequest) requestContext;
cr.bufferEntity();
}
Now you can get the entity. If you look at the API for ContainerRequest, there are also methods to readEntity(..). If you are familiar with the JAX-RS Client API, you may have before used Response#readEntity(...class) to read the response entity. The ContainerRequest#readEntity(..) works pretty much the same way.
So if you know what the JSON format is supposed to be, and you have the POJO, you could do
POJO pojo = cr.readEntity(POJO.class);
Otherwise, if the format will change from request to request, you could extract the data as a map
Map<String, Object> json = cr.readEntity(new GenericType<Map<String, Object>>(){});
UPDATE
If you are using one JAX-RS APIs, and not Jersey specific APIs, then the above is not doable. You will instead need to read the stream to get the JSON, and set the stream back, so that Jersey can read it. If might look something like
InputStream entityIn = requestContext.getEntityStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// write `entityIn` to `baos`
byte[] bytes = baos.toByteArray();
POJO pojo = new ObjectMapper().readValue(bytes, POJO.class);
// do something with POJO
requestContext.setEntityStream(new ByteArrayInputStream(bytes));
Of course you will need to some JSON deserializer to do this. I just used Jackson in the example.
It's not as elegant as the first example, but you don't have much option if you are strictly sticking the JAX-RS APIs. If you can I would suggest just adding the Jersey dependencies to your project as provided (compile-time) so that you can use the APIs, since you are using Jersey with WebLogic anyway.

JSON unmarshalling with Moxy in ContainerResponseFilter

I'm using Moxy in my Jersey (2.7) project basically just to marshall my objects to JSON when the service issues a response. It works fine, but now I am also using ContainerResponseFilter to make some changes on every response issued and I am not sure how to unmarshall the content of the request body into an object, which is something I need.
Specifically:
i've just registered Moxy in a ResourceConfig instance: register(MOXyJsonProvider.class)
a class is using JAXB annotations, so when I set an instance of the class in Response.entity() it gets transformed into JSON properly
the request body (also JSON) also gets unmarshalled into an object when I set it as a method paramether, for instance:
#Consumes(MediaType.APPLICATION_JSON)
public Response getSomething( MyClass instance ) {
However inside a ContainerResponseFilter I can access the request body like so,
InputStream body = requestContext.getEntityStream()
but I'm not sure if it's possible to have this also automatically converted into an object. The information I need is relatively simple, so I guess I could parse JSON in another way, but I'm curious.
I've tried searching, but I didn't find it.
In your ContainerReponseFilter, you can do something like this:
public class ApplicationResponseFilter implements ContainerResponseFilter {
#Override
public void filter(final ContainerRequestContext request,
final ContainerResponseContext response) throws IOException {
// your code
response.getEntity();
}
}
which converts it to your object with JAXB annotations. I was not doing it in my responseFilter, but I've just debugged it and it works.

How to inject dynamic Bean parameter into Jersey method?

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.

Obtaining actual parameter values in a Jersey ResourceFilterFactory

I want to implement custom authorisation in my REST services using Jersey. This custom authorisation inspects annotations on methods as well as the actual parameters that a
method receives.
My jax-rs annotated method looks like:
#GET
#Path("customers")
#Requires(Role.CustomerManager)
public Customer getCustomer(#ParseFromQueryString #CheckPermission final Customer customer) {
// ...
}
The #ParseFromQueryString is an annotation that indicates Jersey (through an Injectable provider) to unmarshall a Customer from a query string. The code for that looks like:
public class QueryStringCustomerInjectable implements Injectable<Customer> {
public Customer getValue() {
final Customer customer = new Customer();
// ... a UriInfo was injected using the #Context annotation
// ... extract parameters from QueryString and use setters
return customer;
}
}
The #CheckPermission annotation indicates my custom authoriser that permissions are to be checked on a customer. Some users have access to information on some customers. Similarly, the #Requires annotation takes a role that the invoker should have. These are not java's security roles (Strings), rather, they are enum values.
Using Jersey's ResourceDebuggingFilter as a starting point, I have been able to get to the point of knowing which method will be invoked. However, I still haven't figured out how to determine which parameters will actually be used to invoke the method.
At the top of my head, I can think of two work arounds:
A Method interceptor using Guice + Jersey.
Code this logic in the QueryStringCustomerInjectable, but this seems a bit sloppy. It would be a class doing too much.
Yet, I would really like to do this using only Jersey / JAX-RS. I feel that I am so close!
Ideas? Pointers?
Thanks!
You should use Filters or Interceptors to handle all the information about method.
see Jersey Filter and Interceptors
For the Customer deserialization you could implement the javax.ws.rs.ext.ParamConverterProvider and register it into Jersey. Then you can inject it into your methods with #QueryParam("customer"). It's a bit more flexible since you can use it also with #BeanParam or #PathParam annotations.
Then you can use the ContainerRequestFilter. See as a reference how jersey does the Oauth1 for example OAuth1ServerFilter.
The next thing you can do is to create maybe a feature which will register the newly created filter (see Oauth1ServerFeature for a reference - I couldn't find the source code right now).
Good luck!
Why not using your own Servlet filter e.g.
public class YourFilter implements Filter {
...
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// HttpServletRequest httpReq = (HttpServletRequest) request;
// HttpServletResponse httpResp = (HttpServletResponse) response;
// HttpServletRequest httpReq = (HttpServletRequest) request;
// HttpServletResponse httpResp = (HttpServletResponse) response;
// ..... httpReq.getUserPrincipal();
// then set what you need using ThreadLocal and use it inside your resource class
// do not forget to call
filterChain.doFilter(request, response); // at the end of this method
}
The last step is to register your servlet filter. This is done using web app's web.xml
It will intercept your HTTP requests before the actual code inside jersey resource is called.

Categories

Resources