I Am using Spring\'s @ExceptionHandler
annotation to catch exceptions in my controllers.
Some requests hold POST data as plain XML string written to the req
I've tried your code and I've found some mistakes in the exception handler, when you read from the InputStream
:
Writer writer = new StringWriter();
byte[] buffer = new byte[1024];
//Reader reader2 = new BufferedReader(new InputStreamReader(request.getInputStream()));
InputStream reader = request.getInputStream();
int n;
while ((n = reader.read(buffer)) != -1) {
writer.toString();
}
String retval = writer.toString();
retval = "";
I've replaced your code with this one:
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line = "";
StringBuilder stringBuilder = new StringBuilder();
while ( (line=reader.readLine()) != null ) {
stringBuilder.append(line).append("\n");
}
String retval = stringBuilder.toString();
Then I'm able to read from InputStream
in the exception handler, it works!
If you can't still read from InputStream
, I suggest you to check how you POST xml data to the request body.
You should consider that you can consume the Inputstream
only one time per request, so I suggest you to check that there isn't any other call to getInputStream()
. If you have to call it two or more times you should write a custom HttpServletRequestWrapper
like this to make a copy of the request body, so you can read it more times.
UPDATE
Your comments has helped me to reproduce the issue. You use the annotation @RequestBody, so it's true that you don't call getInputStream()
, but Spring invokes it to retrieve the request's body. Have a look at the class org.springframework.web.bind.annotation.support.HandlerMethodInvoker
: if you use @RequestBody
this class invokes resolveRequestBody
method, and so on... finally you can't read anymore the InputStream
from your ServletRequest
. If you still want to use both @RequestBody
and getInputStream()
in your own method, you have to wrap the request to a custom HttpServletRequestWrapper
to make a copy of the request body, so you can manually read it more times.
This is my wrapper:
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
private static final Logger logger = Logger.getLogger(CustomHttpServletRequestWrapper.class);
private final String body;
public CustomHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
logger.error("Error reading the request body...");
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
logger.error("Error closing bufferedReader...");
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final StringReader reader = new StringReader(body);
ServletInputStream inputStream = new ServletInputStream() {
public int read() throws IOException {
return reader.read();
}
};
return inputStream;
}
}
Then you should write a simple Filter
to wrap the request:
public class MyFilter implements Filter {
public void init(FilterConfig fc) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new CustomHttpServletRequestWrapper((HttpServletRequest)request), response);
}
public void destroy() {
}
}
Finally, you have to configure your filter in your web.xml:
MyFilter
test.MyFilter
MyFilter
/*
You can fire your filter only for controllers that really needs it, so you should change the url-pattern according to your needs.
If you need this feature in only one controller, you can also make a copy of the request body in that controller when you receive it through the @RequestBody
annotation.