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
Related
I want to know if there is a way of removing those headers from my current response. I am using the #ResponseBody annotation and already have tried using a Filter to try to not add those headers, based on the following How do delete a HTTP response header?.
Ideally, the HTTP response should be like the one from this link: https://api.github.com/users/mralexgray/repos with no Content-Length and Transfer-Encoding headers.
You could write directly to the HttpServletResponse's OutputStream. Spring will give you the HttpServletResponse (and the HttpServletRequest) if you want it, simply by adding it to your method signature.
This way you have (mostly) full control of headers. You would probably need to create the JSON yourself, but it's usually quite simple. For example...
private ObjectMapper mapper = new ObjectMapper();
#RequestMapping(value = "/getStuff", method = RequestMethod.GET)
public void getStuff(HttpServletResponse httpServletResponse) throws Exception {
try {
httpServletResponse.setHeader("Pragma","public");
httpServletResponse.setHeader("Expires","0");
httpServletResponse.setHeader("Cache-Control","must-revalidate, post-check=0, pre-check=0");
httpServletResponse.setHeader("Cache-Control","public");
OutputStream outputStream = httpServletResponse.getOutputStream();
try {
mapper.writeValue(outputStream, myObject);
} finally {
outputStream.close();
}
This might not seem elegant, but by using #ResponseBody you are using that as a convenience to do all the hard work in creating the response. But if it is not creating the response as you would like it, you can take a step back and do it "manually" using HttpServletResponse.
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.
I have an API created with Jersey
There's currently an endpoint to which users can make POST requests. There is no body required, as all the information is in the url.
#POST
#Path("entries")
#Produces(MEDIATYPE_JSON_AND_XML)
public Response createEntry(){
...
}
A new, empty, entry is created and the id is returned.
Content-Type of the request doesn't matter, as there is no request body data.
Now it should also be possible to set specific fields of the new entry during the request, using FormData. For this request a body is necessary, and the Content-Type must be multipart/form-data.
So I've created a second function:
#POST
#Path("entries")
#Consumes("multipart/form-data");
#Produces(MEDIATYPE_JSON_AND_XML)
public Response createEntryWithParam(#FormDataParam('param') String param){
...
}
This second function works te send the parameter in the request. But by adding it, the first stops working.
Sending a request without Content-Type will throw a NullPointerException. Probably because the #Consumes triggers some kind of Content-Type-check, which fails.
Is there a way to have one endpoint accepting POST requests with or without request-body?
edit So, I would like to receive all multipart/form-data requests in the seconds function, and use the first as a kind of catch-all for other POST requests to that endpoint
Currently I have a work-around in place.
If a POST request comes in without MediaType (Content-Type) or request-body, I automatically add an empty JSON object and set the Content-Type accordingly.
#Provider
public class ContentTypeRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext crc) throws IOException {
if (crc.getMethod().equals("POST") && crc.getMediaType() == null && crc.getLength() == -1){
crc.getHeaders().add("content-type", MediaType.APPLICATION_JSON);
InputStream in = IOUtils.toInputStream("{}");
crc.setEntityStream(in);
}
}
}
This works, but is kinda hacky in my opinion. I'm interested to know if there are better ways to achieve my desired result.
So if someone sends in this accept header:
*/*'"!#$^*\/:;.,?{}[]`~-_<sCrIpT>alert(81363)</sCrIpT>
it is returned in the response using Jersey. I need to intercept this and escape it. I have not found any way to do this. Any help?
You could use filters to intercept your request and do the cleanup before they reach your service class. Check this example.
You can read request headers, and modify response headers in this way (you can not modify request headers, it's a read only map):
#Context HttpHeaders headers;
#Path("yourPath")
public Response yourMethod(){
List<String> accept = headers.getRequestHeader(HttpHeaders.ACCEPT);
if ( accept contains invalid characters)) {
do something
}
//If you set a null value, the header is deleted.
//Anyway, deletion is useless, if you have not yet set
// the Accept header on the response.
return Response.ok().header(HttpHeaders.ACCEPT, null).build();
}
I've created a HttpServer based on com.sun.net.httpserver.HttpExchange.
in my handler, impementation of com.sun.net.httpserver.HttpHandler, the method public void handle(HttpExchange exchange) gets an exchange with empty requestBody.
On the server side i'm using:
server.createContext("/", new BbHandler());
On my Midlet client i'm using:
String url = "http://localhost:22334/name=john";
Why exchange's requestBody has no data to read?
Thanks,
Eyal.
The most likely cause is that there is no body to be read. Your provided field is in the URL and would be available in the getRequestURI() call on the exchange object (though it probably won't be in the getQuery() part of the URI object because it is missing the ? separator the URI would expect for query parms), but nothing in the body. Most often a GET request does not have body contents, a PUT or POST request would.