I'd like to be able to write an entire request to a file within a Spring MVC controller.
I've tried the following, but the file is always empty even though I'm making a POST request with loads of parameters:
@RequestMapping(method = RequestMethod.POST, value = "/payments/confirm")
public void receiveCallback(ServletInputStream inputStream)
{
try
{
inputStream.reset();
byte[] data = IOUtils.toByteArray(inputStream);
File file = new File(System.getProperty("java.io.tmpdir") + "test" + System.currentTimeMillis() + ".txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
}
catch (Exception e)
{
logger.error("Error writing request", e);
}
}
I've also tried using HttpServletRequest.getInputStream(), but the same results.
Using the InputStream won't work (see BalusC's answer). Here's a sample of how you could use a HTTPServletRequest object instead to write headers and parameters:
@RequestMapping(method = RequestMethod.POST, value = "/payments/confirm")
public void receiveCallback(HttpServletRequest request) {
try {
StringBuilder sb = new StringBuilder();
sb.append("Headers:\n");
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
Enumeration<String> headers = request.getHeaders(headerName);
while (headers.hasMoreElements()) {
String headerValue = headers.nextElement();
sb.append(headerName).append(':').append(headerValue).append('\n');
}
}
sb.append("\nParameters:\n");
for(Entry entry: (Set<Entry>) request.getParameterMap().entrySet(){
sb.append(entry.getKey()).append(':').append(entry.getValue()).append('\n');
}
byte[] data = sb.toString().getBytes();
File file = new File(System.getProperty("java.io.tmpdir") + "test"
+ System.currentTimeMillis() + ".txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
} catch (Exception e) {
logger.error("Error writing request", e);
}
}
I know nothing about Spring, but I can at least tell that the POST request body can be read only once. It's namely the data whatever the client has sent to the server. The client ain't going to resend it multiple times whenever the server needs it multiple times.
I assume that Spring has already read the request body in order to parse the enclosed query string and get the request parameters before entering the Spring controller method. This can under Spring's covers be done with request.getParameter()
and consorts. In the Servlet API, once that method is been called, the request.getInputStream()
and request.getReader()
won't return anything afterwards. Simply because the request body is already been read in order to return the parameters. This is also mentioned in the getParameter()
's javadoc.
If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via
getInputStream()
orgetReader()
can interfere with the execution of this method.
Your best bet is to create a filter which makes a copy of the request body before Spring does its job and then place the filter in front of the Spring controller. Making a copy of the request body is possible with help of a HttpServletRequestWrapper
wherein you override getInputStream()
and getReader()
methods to first read the request body into a ByteArrayInputStream
and/or a CharArrayReader
so that you have a local copy and then return it instead. A reference to the HttpServletRequestWrapper
could be stored as a request attribute so that you can obtain it as request attribute in the Spring controller and finally get the copy of the request body.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With