Spring Boot 使用ContainerResponseFilter获取响应体

insrf1ej  于 2023-04-20  发布在  Spring
关注(0)|答案(2)|浏览(238)

我尝试使用ContainerResponseContextgetEntityStream()捕获响应实体流。下面是代码:

public String getResponseEntityStream(ContainerResponseContext context) throws IOException {

    OutputStream originalStream = context.getEntityStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    context.setEntityStream(baos);

    String body = baos.toString();
    baos.writeTo(originalStream);
    baos.close();
    context.setEntityStream(originalStream);
    return body;
}

我在下面的代码中调用上面的方法来获取整个响应流:

@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
        throws IOException {

    StringBuilder sbResponse = new StringBuilder();
    
    sbResponse.append(" - Response Entity Class: ").append(responseContext.getEntityClass())
        .append(System.getProperty("line.separator"));

    sbResponse.append(" - Response Entity Stream: ").append(getResponseEntityStream(responseContext))
        .append(System.getProperty("line.separator"));

    System.out.println("HTTP RESPONSE : " + "\n" + sbResponse.toString());
}

我的问题:

在上面的代码中,Response Entity Stream总是空的。我只是试图读取整个响应实体流并打印它以显示原始流的样子,然后再对流进行一些处理。我在哪里犯了错误?提前感谢。

**UPDATE:**我可以使用WriterInterceptor获取响应体。但是只有当有响应负载时才会调用WriterInterceptor。我使用ContainerResponseFilter是因为我想在没有负载的情况下读取响应详细信息。此外,Writer Interceptor不像ContainerResponseFilter那样帮助getStatus()getStatusInfo()。有没有方法可以将ContainerResponseFilterWriterInterceptor的数据组合在一个字符串中并在日志中打印整个响应。可能是使用Thread Local?

t8e9dugd

t8e9dugd1#

ContainerResponseContext有两个方法在这里被混淆:

  • getEntity()-返回实际的Java实体对象,然后再写入它
  • getOutputStream()-返回用于写入实体的OutputStream。实体在编写器拦截器阶段写入。

OutputStream可以由用户提供的OutputStream进行修饰,该OutputStream可以执行所需的操作,例如,记录信息并将数据写入原始流。这个新的修饰器OutputStream可以通过ContainerResponseContext#setEntityStream()注册。
Jersey附带LoggingFeature /ServerLoggingFilter/LoggingInterceptor。它们一起完成了这个日志记录,过滤器装饰了原始的OutputStream,它们记录了实体,头和其他与响应相关的信息。
在这些Jersey类中,您可以看到如何使用ContainerRequestContext#setProperty()和WriterInterceptorContext#getProperty将信息从Filter传递到Interceptor

bxjv4tth

bxjv4tth2#

我认为对ContainerResponseContext#getEntityStream()方法的用法有一个误解。人们不应该从OutputStream读取。
正如@jan-supol在他的回答中所说,例如,您可以使用记录器装饰OutputStream
但是,如果您仍然希望以现在的方式记录响应,则可以直接检索实体。
你写的代码:

OutputStream originalStream = context.getEntityStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // it's empty
    context.setEntityStream(baos); // why? you're replacing the original os by this one
    String body = baos.toString(); // empty stream, empty string
    baos.writeTo(originalStream); // you're writing your empty stream to os, which doesn't affect the os
    baos.close();
    context.setEntityStream(originalStream); // assignment rollback?
    return body; // returning an empty string

我会这么做

@Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response) {
        if (response.hasEntity()) {
            StringBuilder sb = new StringBuilder();
            sb.append(" - Response Entity Class: ").append(response.getEntityClass())
                    .append(System.getProperty("line.separator"));
            // in order to work, you should guarantee that entity has reasonable #toString implementation
            Object entity = response.getEntity();
            sb.append(" - Response Entity Stream: ").append(entity.toString())
                    .append(System.getProperty("line.separator"));
            System.out.println("HTTP RESPONSE : " + "\n" + sb.toString());
        }
    }

相关问题