Java 在异常映射器中检索请求正文

Java 在异常映射器中检索请求正文,java,servlets,jersey,jax-rs,Java,Servlets,Jersey,Jax Rs,我试图在JAX-RS ExceptionMapper中检索请求主体。以下是我目前的代码: @Provider @Componenet public class BaseExceptionMapper implements ExceptionMapper<Exception> { @Context private HttpServletRequest request; @Override public Response toResponse(Exceptio

我试图在JAX-RS ExceptionMapper中检索请求主体。以下是我目前的代码:

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

    }

}
@Provider@Componenet
公共类BaseExceptionMapper实现ExceptionMapper{
@上下文私有HttpServletRequest;
@凌驾
公众回应(例外情况除外){
//试图检索日志记录的请求正文时抛出错误
字符串requestBody=IOUtils.toString(request.getInputStream());
}
}

因此,我的困境是无法获取用于日志记录的请求主体,因为servlet API不允许对请求多次调用request.getInputStream()/request.getReader()(JAX-RS显然是调用它来解析请求)。有人知道有没有办法完成我的任务吗?

一个可能的解决方案是使用servlet过滤器并包装请求,这允许您拦截对请求输入流的读取调用。伪代码示例(取决于
公用io
):

过滤器:

public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    MyHttpRequest wrapper = new MyHttpRequest((HttpServletRequest) request);
    chain.doFilter(wrapper, response, chain);
}
制图员:

@Context private HttpServletRequest request;
@Override public Response toResponse(Exception ex) {
    String body = "";
    if (this.request instanceof MyHttpRequest) {
        body = ((MyHttpRequest)request).getRequestBody()
    }
}

ServletInputStream
需要一个包装类,您可以在这里找到一个示例实现:

这个问题有点老,但答案可能对其他人有所帮助。我的例子也依赖于Commons Io

您可以创建ContainerRequestFilter并使用TeInputStream代理/复制原始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);
    }

}
并在ExceptionMapper中使用@Inject with javax.Inject.Provider来注入ContainerRequest

ExceptionMapper如下所示:

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

}
@Provider
公共类BaseExceptionMapper实现ExceptionMapper{
@注入
private javax.inject.Provider containerRequestProvider;
@凌驾
公众响应(例外){
ByteArrayOutputStream bos=(ByteArrayOutputStream)containerRequestProvider
.get().getProperty(“实体\流\副本”);
字符串requestBody=bos.toString();
...
}
}

当我还使用@Component注释时,我的ExceptionMapper没有被使用。我认为@Provider就足够了。

我想您需要创建自定义异常,将已解析的实体包含在其中,并在ExceptionMapper中重用该值。
@friedea
这是唯一的返回请求体,但是我需要所有可能的信息来发送包含所有细节的邮件,比如:1>请求URL 2>请求方法3>请求正文4>路径参数5>查询参数6>IP地址等。这不是泄漏了输出流吗?它什么时候关闭?
setEntityStream
文档说:“JAX-RS运行时负责关闭输入流”我已经尝试了您的解决方案,但在
toResponse
方法中,
request
从来不是
MyHttpRequest
的实例。相反,
request
是的一个实例,我无法找到将其强制转换为所需类的方法。有什么想法吗?@Fredericopantuzzademira你确定你的过滤器正在运行吗?代理类通常由框架生成,但是您的过滤器应该包装生成的类。您也可以尝试创建一个接口,并在过滤器中提供一个实现,但也可以仅引用该接口。也请查看
@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();
        ...
    }

}