Remove Content-Length and Transfer-Encoding Headers from spring servlet HTTP Response - java

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.

Related

Spring - how to pass automatically http header between microservices

I'm having multiple microservices
1. MangerApp
2. ProcessApp
3. DoingStuffApp
4. .....
the "MangerApp Microservices" get an Http-Request
I'm looking for a way to transfer automatically some of the HTTP headers
in the call, while I don't want to go over each place and do - add Headers, my HTTP headers are stored as a thread-local Map.
since I call to other microservices, with RestTemplate I have many different calls some get/post/put/etc...
changing each one them and passing the header manually is not that efficient.
I'm looking for a way to manage it, other than extending the RestTemplate Class now.
You can use a ClientHttpRequestInterceptor to achieve what you need.
1) Create a HeaderInterceptor implementing ClientHttpRequestInterceptor. In this example it gets the Authorization and Accept headers from a ThreadLocal and propagates them:
public class HeaderInterceptor implements ClientHttpRequestInterceptor{
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
List<String> authorization = HeaderThreadLocal.getAuthorization()
List<String> accept = HeaderThreadLocal.getAuthorization();
headers.addAll("Authorization", authorization);
headers.addAll("Accept", accept);
return execution.execute(request, body);
}
}
2) Configure your RestTemplate bean adding the header interceptor:
restTemplate.getInterceptors().add(new HeaderInterceptor());

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.

Multipart Request with MultipartFile as Optional Field - Spring MVC

I am using Spring MVC on a J2EE Web application.
I have created a method that bounds the request body to a model like the above
#RequestMapping(value = "/", method = RequestMethod.POST, produces = "application/json")
public AModel createEntity(#Valid #ModelAttribute MyInsertForm myInsertForm) {
// coding..
}
Everything are working great and when i include a property of type MultipartFile in the MyEntityForm, then i have to make the request with content type "multipart/form-data".
Also, everything are working great with this scenario too.
The problem i am facing is that i would like to have the MultipartFile property as optional.
When a client request include a file my method works great but when a client request does not include a file spring throws a
HTTP Status 500 - Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadException: Stream ended unexpectedly
Is there any way to solve this issue without creating two methods on my controller (one with a MultipartFile and another without)?
I had the same issue and just adding the required=false worked for me; because, I don't send a file all the time. Please find the sample code below,
#RequestMapping(value = "/", method = RequestMethod.POST, produces = "application/json")
public AModel createEntity(#Valid #ModelAttribute MyInsertForm myInsertForm, #RequestParam(value ="file", required=false) MultipartFile file) {
// coding..
}
Give a try by adding
(required=false)
to multipart property in method signature.
When you wish to send one or more files using HTTP, you have to use multipart request. This means that the body of the request will be like the above,
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="text"
text default
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
Content of a.txt.
-----------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html
When you wish to send only data (and not files) you can send them as json, key-value pairs etc.
Spring framework uses the #ModelAttribute annotation when you wish to map a multipart request to an object.
When you have a normal key-value request, you use the #RequestBody annotation.
Thus, you can't have the MultipartFile optional, because you have to use different annotations. Using two different methods, one per request type, solves the issue. Example,
#RequestMapping(value = "/withFile", method = RequestMethod.POST, produces = "application/json")
public ReturnModel updateFile(#ModelAttribute RequestModel rm) {
// do something.
}
#RequestMapping(value = "/noFile", method = RequestMethod.PUT, produces = "application/json")
public ReturnModel updateJson(#RequestBody RequestModel rm) {
// do something else.
}

Is there a way to "clean" an accept header in Java?

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();
}

Categories

Resources