Spring interceptor read response OutputStream - spring-mvc

Inside spring controller writes an excel stream into response
HSSFWorkbook workbook = getWorkbook();
OutputStream out = response.getOutputStream();
response.setHeader("pragma", "public");
response.setHeader("Cache-Control", "public");
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=sampleexcel.xls");
workbook.write(out);
out.close();
// response.flushBuffer();
As per this link How to read and copy the HTTP servlet response output stream content for logging
implementated responsewrapper.
Below is Interceptor code,
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
HttpServletResponseCopier resp= new HttpServletResponseCopier(response) ;
byte[] responseData = resp.getCopy();
System.out.println("length "+responseData.length); // its 0 bytes
}
Basically want to read the Outputstream contents into a temp file. Then add encryption information in it. Finally write this encrypted file into response stream.
In above code,resp.getCopy() is empty,hence it writes 0 bytes into temp file.
Any pointers what is wrong.Is there alternate way to do achive this.
Spring 3.1, JDK 1.7

Oups, a spring-mvc interceptor is not a filter. It provides hooks to be called before controller execution, after controller execution and after view generation, but cannot replace the response.
The filter used in the referenced post actually replace the response with a wrapper so that everything that is written to the response actually goes into the wrapper and is processed at the time it is written. Here you only create the wrapper once everything has been written so it can only intercept... nothing.
You will have to implement a filter and a custom response wrapper to achieve your goal.

Related

jetty server log request body

RequestLogHandler requestLogHandler = new RequestLogHandler();
Slf4jRequestLog requestLog = new CustomSlf4jRequestLog();
requestLogHandler.setRequestLog(requestLog);
Slf4jRequestLog is only logging request method, url and date, and response status code and bytes written.
I definitely want to log body for my PUT/POST requests.
I derived CustomSlf4jRequestLog from Slf4jRequestLog and I tried:
public void log(Request request, Response response) {
StringBuilder sb = new StringBuilder("RequestBody: ");
try {
LOG.info("BODY SIZE: " + request.getContentLength());
BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
LOG.info(sb.toString());
Unfortunately no body is printed out as it is already processed by handler?
Is it possible to get body of request here?
(I really care about body because I have JsonProvider and I want to see a whole body of request when Json fails to parse data) Or when my app fails I want to see what caused that without adding logging for each input request.
Servlet spec getting in your way here.
The actual servlet that is processing the request has already read the request body, rending further reads from request.getInputStream() invalid (you are at EOF)
If you want to capture the request body, you'll need to create a custom Servlet Filter, and then wrap the HttpServletRequest, overriding the getInputStream() AND getReader() methods with your own implementations that make a copy of the data that was read.
Then its up to you to determine what you want to do with that copy of the request body content.
or
You can just use a network capture utility like Wireshark to see what the request body was. Even if you use HTTPS, you can configure Wireshark with your server certificate to inspect encrypted conversation.
The logger is calling getInputStream() on the same request again. You are not allowed to read the same data twice. Yo should create a ServletRequestWrapper to make a copy of the body of the request.

Send parameter from servlet to servlet

I have a servlet load_plan, which its doPost method loads a plan with this code:
int id = Integer.parseInt(request.getParameter("id"));
I have another servlet hire_new_plan, which its doGet method sends the id to the load_plan servlet.
hire_new_plan doPost() --> load_plan doGet().
How can I send the id from the doGet method so that I can capture it with the request.getParameter() in the doPost method of the receiver servlet?
There are two problems as I have read.
First of all, I can't call the doPost method from another servlet's doGet method.
Secondly, it seems as if the request.setAttribute("id", id) doesn't match with the int id = Integer.parseInt(request.getParameter("id"));. I execute in the receiver servlet.
What can I do to fix this?
You can use RequestDispatcher to forward to other servlet.
RequestDispatcher rd = request.getRequestDispatcher("load_plan");
rd.forward(request, response);
//or rd.include(request, response);
While forwarding same request object is sent to the called servlet.So you do not need to do anything.It will be available in that request object by default.
If you go this way,doGet will get called of another servlet.
If you can't move your logic in that,I would suggest you to use HttpURLConnection object.You can fire POST request programmatically like this
HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost:8080/WebAppName/SecondServlet").openConnection();
connection.setRequestMethod("POST");
InputStream response = connection.getInputStream();
// ... Write to OutputStream of your HttpServletResponse
See More
Using java.net.URLConnection to fire and handle HTTP requests

How to Read the message Body in a HTTP POST in Netty (version >= 4.x)

In my server handler;
- channelRead() always gets the msg as HTTPRequest and there I cannot find any place to get the POST request payload.
Then I tried following in my handler to check if it works. The decoder has 0 elements.
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
In my server pipeline I have HttpServerCodec and a custom handler only.
It's likely that your HTTP request is chunked. You should try to add an io.netty.handler.codec.http.HttpObjectAggregator to your pipeline, just after the codecs. It will give a FullHttpRequest to your handler.
ChannelPipeline p = ...;
...
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("aggregator", new HttpObjectAggregator(1048576));
...
p.addLast("handler", new MyServerHandler());
Alternatively, you could check this example where HttpRequest and HttpContent are handled separately.
As Leo Gomes mentioned, HTTP Request maybe chunked. so add HttpObjectAggregator before your own handler in pipeline.
if HTTP POST request body is Simple Json String. You can parse it in your own handler like this:
private String parseJosnRequest(FullHttpRequest request){
ByteBuf jsonBuf = request.content();
String jsonStr = jsonBuf.toString(CharsetUtil.UTF_8);
return jsonStr;
}

Spring MVC with HttpPutFormContentFilter can't get RequestBody when use PUT method

I have added the HttpPutFormContentFilter in web.xml.
Here is an action for reveiving both GET,POST,DELETE and PUT method.
#RequestMapping(value = "/**")
public ResponseEntity<byte[]> proxy(HttpServletRequest request,#RequestParam MultiValueMap<String, String> params, #RequestBody byte[] body, #RequestHeader MultiValueMap<String, String> headers) {
When I use POST and add the application/x-www-form-urlencoded header,I can get both the request body and request param.
When I use PUT and add the application/x-www-form-urlencoded header,I can get the request param,but I can't get request body!
There is any bug in the HttpPutFormContentFilter?
According to the Servlet specification (see chapters 1.1 and 3.1.1), when you receive a POST request and the content type is application/x-www-form-urlencoded, that form data needs to be made available through the HttpServletRequest#getParameterXXX() methods. This is not true for PUT requests.
In all cases, the body of the HttpServletRequest is available as an InputStream to Servlet and Filter instances.
For POST, when Spring sees
#RequestParam MultiValueMap<String, String> params
it uses a RequestParamMapMethodArgumentResolver to resolve the argument. This reads directly from the HttpServletRequest parameter map. When Spring sees
#RequestBody byte[] body
it uses a RequestResponseBodyMethodProcessor which reads from the HttpServletRequest InputStream using a ByteArrayHttpMessageConverter to fill up a byte[].
Once you've read the HttpServletRequest InputStream, you cannot re-read (in the default configuration).
For PUT, because the Servlet container doesn't store form parameters in the HttpServletRequest for PUT requests, Spring decided to introduce the HttpPutFormContentFilter. This Filter reads the HttpServletRequest body to populate its own parameter map in an HttpServletRequestWrapper which it hands you.
Once this is done, the request parameters are available to the RequestParamMapMethodArgumentResolver, but when the RequestResponseBodyMethodProcessor tries to fill up the byte[], there are no bytes left in the InputStream so it leaves it empty.
One workaround is to create your own Filter (which must execute before the HttpPutFormContentFilter, so it's kind of bad practice), which wraps the HttpServletRequest in a HttpServletRequestWrapper which buffers the InputStream in a ByteArrayInputStream so you can re-read it as many times as necessary.

RequestDispatcher ending in infinite loop

I have two WAR applications and the mode of communication between them is via servlets.
My application (WAR A) opens a child window with the URL of a servlet in another WAR (lets say WAR B).
The servlet (in WAR B) processes the data and should send the processed data back to original application's servlet (i.e WAR A's servlet).
But this process ends in an infinite loop and also the URL parameters sent from WAR-A are null.
Here is the code snippet :
The below script opens a child window with the URL of servlet in WAR-B also passing some URL parameters.
function invokePlugin(invokeURL, custValJSON, meaCompPartJSON) {
window.open(invokeURL + '?custValJSON=' + custValJSON,'');
}
Below is servlet code in WAR-B which extracts the URL parameters and process the data and again send the request back to WAR-A's servlet...
private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String custValJSON = request.getParameter("custValJSON");
System.out.println("custValJSON : " + custValJSON);
CustomValues custVal = gson.fromJson(custValJSON, CustomValues.class);
if(custVal != null) {
System.out.println("Cust val details : " + custVal.getName());
custVal.setValue("Satya");
}
String destination = "/testPlannerPluginResult";
RequestDispatcher reqDispatch = request.getRequestDispatcher(destination);
request.setAttribute("custValJSON", gson.toJson(custVal));
if(reqDispatch != null) {
reqDispatch.forward(request, response);
}
}
Does anybody have idea on this?
Regards,
Satya
That then just means that the servlet is basically calling itself everytime. I don't immediately see the cause in the information given so far, but apparently the URL which you passed into the getRequestDispatcher() matches the URL of the servlet itself.
I however see a major mistake here:
RequestDispatcher reqDispatch = request.getRequestDispatcher(destination);
request.setAttribute("custValJSON", gson.toJson(custVal));
That can impossibly invoke the servlet which runs in another servlet context (read: another WAR). You need ServletContext#getContext() first to get the other servlet context and then use ServletContext#getRequestDispatcher() to dispatch the request to there.
ServletContext otherServletContext = getServletContext().getContext("/otherContextPath");
RequestDispatcher dispatcher = otherServletContext.getRequestDispatcher(destination);
This only requires that the both WARs are configured to expose the context for sharing. On Tomcat for example, this is to be done by adding crossContext="true" to the <Context>.

Resources