Reputation: 9487
I'm trying to retrieve the body of a request in a JAX-RS ExceptionMapper. Here is my code so far:
@Provider @Componenet
public class BaseExceptionMapper implements ExceptionMapper<Exception> {
@Context private HttpServletRequest request;
@Override
public Response toResponse(Exception ex) {
// Trying to retrieve request body for logging throws an error
String requestBody = IOUtils.toString(request.getInputStream());
}
}
So my dilemma is I can't get the request body for logging because the servlet API wont allow you to call request.getInputStream() / request.getReader() more than once for a request (and JAX-RS Is obviously calling it to parse the request). Does anyone know if there is a way to do what I'm trying to do?
Upvotes: 15
Views: 10928
Reputation: 442
I know this is an old question but I found a workaround that I think it's nice to share.
With the following code you should be able to get the ContainerRequestContext
inside the ExceptionMapper
, then you can read the body, query params, headers, etc.
@Provider
public class CustomExceptionMapper implements ExceptionMapper<CustomException> {
@Context
private ResourceContext resourceContext;
@Override
public Response toResponse(CustomException e) {
ContainerRequestContext requestContext =
resourceContext.getResource(ContainerRequestContext.class);
}
}
Hope it can help
Upvotes: 0
Reputation: 1769
One possible solution is to use a servlet filter and wrap the request, which allows you to intercept read calls to the request input stream. Example pseudo-code (depends on commons-io
):
import org.apache.commons.io.output.StringBuilderWriter;
import org.apache.commons.io.input.TeeInputStream;
class MyHttpRequest extends HttpServletRequestWrapper {
private StringBuilderWriter myString = new StringBuilderWriter();
private InputStream myIn;
public MyHttpRequest(HttpServletRequest request) {
super(request);
myIn = new TeeInputStream(request.getInputStream(), myString);
}
@Override public ServletInputStream getInputStream()
throws java.io.IOException {
// this will need an extra wrapper to compile
return myIn;
}
public String getRequestBody() {
return myString.toString();
}
}
Filter:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
MyHttpRequest wrapper = new MyHttpRequest((HttpServletRequest) request);
chain.doFilter(wrapper, response, chain);
}
Mapper:
@Context private HttpServletRequest request;
@Override public Response toResponse(Exception ex) {
String body = "";
if (this.request instanceof MyHttpRequest) {
body = ((MyHttpRequest)request).getRequestBody()
}
}
You'll need a wrapper class for ServletInputStream
, and you can find an example implementation here: Modify HttpServletRequest body
Upvotes: 0
Reputation: 131
This question is a bit older, but still the answer may help others. My Example also depends on Commons-Io.
You can create a ContainerRequestFilter and use TeeInputStream to proxy/copy the original InputStream:
@Provider
@Priority(Priorities.ENTITY_CODER)
public class CustomRequestWrapperFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
ByteArrayOutputStream proxyOutputStream = new ByteArrayOutputStream();
requestContext.setEntityStream(new TeeInputStream(requestContext.getEntityStream(), proxyOutputStream));
requestContext.setProperty("ENTITY_STREAM_COPY", proxyOutputStream);
}
}
And use @Inject with javax.inject.Provider in your ExceptionMapper to get the ContainerRequest injected.
The ExceptionMapper would look like this:
@Provider
public class BaseExceptionMapper implements ExceptionMapper<Exception> {
@Inject
private javax.inject.Provider<ContainerRequest> containerRequestProvider;
@Override
public Response toResponse(Exception exception) {
ByteArrayOutputStream bos = (ByteArrayOutputStream) containerRequestProvider
.get().getProperty("ENTITY_STREAM_COPY");
String requestBody = bos.toString();
...
}
}
When I have also used the @Component annotation my ExceptionMapper was not used. I think that @Provider is sufficient.
Upvotes: 12