JSON unmarshalling with Moxy in ContainerResponseFilter - java

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.

Related

Modifying HTTP response body and message

I need to be able to modify the HTTP response body of the response that I am getting when someone hits my Service API. I tried using ConatinerResponseFilter to modify the body, but I believe it will only modify the headers and not the response body. Can someone tell me how I can modify the HTTP response body ,message and the status?
It could be achieved with a WriterInterceptor:
#Provider
public class CustomWriterInterceptor implements WriterInterceptor {
#Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
OutputStream outputStream = context.getOutputStream();
// Manipulate the HTTP entity using the OutputStream
context.setOutputStream(outputStream);
context.proceed();
}
}
In this answer you will find an example of how to modify a JSON sent in the request payload using Jackson (the same idea can be used to manipulate response payload).
The trick is to use a wrapper because body when read as a stream becomes in accessible
Modify HttpServletRequest body
Check this or just check online for modify body in filter
Note :if u are doing a web service then using frameworks like CXF makes it easy modify

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.

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

Annotating resource to produce JSON, but return "text/plain" in response header

I am currently implementing a web API
Spring
Jersey
com.thetransactioncompany.cors http://software.dzhuvinov.com/cors-filter.html
The output (if any) will be JSON, so all my classes are annotated with the expected media type.
#Produces(MediaType.APPLICATION_JSON)
public class CustomerResource {
...
}
that way my classes are automatically transformed to json.
BUT...
Due to microsoft, their IE only support CORS, if the request/response type is text/plain http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
4. Only text/plain is supported for the request's Content-Type header
so I need to force my application to respond with text/plain in the header but still projecting my classes to json output. I know that the CORS classes I added is setting that header, but somehow that is overwritten again by my annotation, even if I add another filter by my own.
Hum, the link you are pointing says that it is true for REQUESTS only.
So you can accept only text plain but are free to produce what ever you want.
EDIT Try registering a custom responsefilter with code similar to that (maybe you already did it?):
#Provider
public class HeaderRewriteFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
response.setResponse(Response
.fromResponse(response.getResponse()).header(HttpHeaders.CONTENT_TYPE, "text/plain").build());
return response;
}
}
However check the result to ensure it is ok if the response already contains this header.
Otherwise you can try to modify the current response, but I am not sure you can as it might be an immutable object. And by the way it looks less clean to me :)
List<Object> contentTypes = response.getHttpHeaders().get(HttpHeaders.CONTENT_TYPE);
contentTypes.clear();
contentTypes.add("text/plain");
Also for doing json<>java databiding you can check Genson library http://code.google.com/p/genson/, it integrates well with Jersey. Just drop the jar in the classpath and run!
EDIT 2 OK then you must do it in the other way, use produces "text/plain" and define a json bodywriter for for that type. Downside is that you will be able to produce only json. With Genson you could do it that way:
#Provider
#Produces({ MediaType.TEXT_PLAIN })
public class PlainTextJsonConverter extends GensonJsonConverter {
public GensonJsonConverter() {
super();
}
public GensonJsonConverter(#javax.ws.rs.core.Context Providers providers) {
super(providers);
}
}

How to pre-process the request body in Spring before passing it to the a Controller?

I am implementing a RESTful service and I would like to validate the XML against an XSD in an interceptor before passing it on the a CastorUnmarshaller.
Though, in the WebRequestInterceptor I have to read the request body which can only be read once so the unmarshaller cannot read it. Is there a way of doing it?
I know that I can do both the validation and the unmarshalling manually in the Controller, but I would like to use the #RequestBody <DomainObject> way to unmarhall it.
Alternatively, as another solution, is there a way to tell the CastorUnmarshaller to validate it against the xsd?
Quite a long time passed, but someone else might benefit from this:
You can define an #Around aspect and intercept the incoming requests and their respective bodies as follows:
#Aspect
#Component
public class RequestResponseLoggingAdvice {
private static final Logger logger = LoggerFactory.getLogger(RequestResponseLoggingAdvice.class);
#Pointcut("within(#org.springframework.web.bind.annotation.RestController*)")
public void restcontroller() {}
#Pointcut("#annotation(org.springframework.web.bind.annotation.PostMapping)")
public void postmapping() {}
#Around("restcontroller() && postmapping() && args(.., #RequestBody body, request)")
public Object logPostMethods(ProceedingJoinPoint joinPoint, Object body, HttpServletRequest request) throws Throwable {
logger.debug(request.toString()); // You may log request parameters here.
logger.debug(body.toString()); // You may do some reflection here
Object result;
try {
result = joinPoint.proceed();
logger.debug(result.toString());
} catch(Throwable t) {}
}
}
Please note that your REST controller methods have to have suitable signatures for the above aspect can hook in. A sample one could be as follows:
#PostMapping
public SampleDTO saveSample(#RequestBody Sample sample, HttpServletRequest request) {
//.....
}
You can probably attach a #Before aspect (spring AOP). There you can get the same request body parameter as passed to the controller method.
Another option is to wrap the request into one that supports reading the body multiple times (by caching it the first time)
A filter can also be used to validate the XML passed.
org.springframework.oxm.castor.CastorMarshaller has a validating property to enable validation on in- and out-going documents.
But enabling it in Spring-MVC's default marshaller must be solved.

Categories

Resources