有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

基于CXF JAXRS的服务中的java限制输出有效负载响应

我使用cxf/spring构建了多个jax-rs服务。我想控制所有服务的输出有效负载响应大小。为了简单起见,假设任何服务中的api都不应返回超过500个字符的JSON响应负载,我希望将其控制在一个地方,而不是依赖单个服务来遵守此要求。(我们已经在所有服务所依赖的定制框架/基础组件中内置了其他功能)

我已经尝试过使用JAX-RS的WriterInterceptorContainerResponseFilter和CXF的Phase Interceptor实现这一点,但这些方法似乎都不能完全满足我的要求。有关我迄今为止所做工作的更多详细信息:

选项1:(Writerineceptor)在重写的方法中,我获取ouputstream并将缓存的最大大小设置为500。当我调用在响应负载中返回超过500个字符的api时,我会得到一个HTTP 400错误的请求状态,但响应体包含整个JSON负载

@Provider
public class ResponsePayloadInterceptor implements WriterInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(ResponsePayloadInterceptor.class);

    @Override
    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
        final OutputStream outputStream = context.getOutputStream();

        CacheAndWriteOutputStream cacheAndWriteOutputStream = new CacheAndWriteOutputStream(outputStream);
        cacheAndWriteOutputStream.setMaxSize(500);
        context.setOutputStream(cacheAndWriteOutputStream);

        context.proceed();
    }
}

选项2a:(CXF-Phase-Inteceptor)在重写的方法中,我从ouputstream获取字符串形式的响应,并检查其大小。如果大于500,我将创建一个新的响应对象,其中只包含数据过多的数据,并在消息中设置它。即使响应为>;500个字符,我得到整个JSON的HTTP 200OK状态。只有当我将该阶段用作POST_MARSHAL或更晚的阶段时,我才能获得JSON响应并检查其长度,但此时响应已经流式传输到客户端

@Provider
public class ResponsePayloadInterceptor extends AbstractPhaseInterceptor<Message> {
    private static final Logger LOGGER = LoggerFactory.getLogger(ResponsePayloadInterceptor.class);

    public ResponsePayloadInterceptor() {
        super(Phase.POST_MARSHAL);
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        LOGGER.info("handleMessage() - Response intercepted");
        try {
            OutputStream outputStream = message.getContent(OutputStream.class);
...
            CachedOutputStream cachedOutputStream = (CachedOutputStream) outputStream;
            String responseBody = IOUtils.toString(cachedOutputStream.getInputStream(), "UTF-8");
...
            LOGGER.info("handleMessage() - Response: {}", responseBody);
            LOGGER.info("handleMessage() - Response Length: {}", responseBody.length());
            if (responseBody.length() > 500) {
                Response response = Response.status(Response.Status.BAD_REQUEST)
                                            .entity("Too much data").build();
                message.getExchange().put(Response.class, response);
            }
        } catch (IOException e) {
            LOGGER.error("handleMessage() - Error");
            e.printStackTrace();
        }
    }
}

选项2b:(CXF相位接收器)同上,但仅更改if块的内容。若响应长度大于500,我将使用字符串创建一个新的输出流,并在消息中设置它。但是如果响应有效负载为>;500个字符,我仍然得到一个HTTP 200OK状态,带有无效的JSON响应(整个JSON+附加文本),即响应如下:[{"data":"", ...}, {...}]Too much data(文本“太多数据”附加到JSON)

        if (responseBody.length() > 500) {
            InputStream inputStream = new ByteArrayInputStream("Too much data".getBytes("UTF-8"));
            outputStream.flush();
            IOUtils.copy(inputStream, outputStream);

            OutputStream out = new CachedOutputStream();
            out.write("Too much data".getBytes("UTF-8"));
            message.setContent(OutputStream.class, out);
        }

选项3:(ContainerResponseFilter)使用ContainerResponseFilter,我添加了一个值为500的Content-Length响应头。如果响应长度为>;500,我得到一个带有无效JSON响应的HTTP 200OK状态(被截断为500个字符)。如果响应长度为<;500,仍然获得HTTP 200OK状态,但客户机等待服务器返回更多数据(如预期)并超时,这不是理想的解决方案

@Provider
public class ResponsePayloadFilter implements ContainerResponseFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(ResponsePayloadFilter.class);

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        LOGGER.info("filter() - Response intercepted");
        CachedOutputStream cos = (CachedOutputStream) responseContext.getEntityStream();
        StringBuilder responsePayload = new StringBuilder();
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        if (cos.getInputStream().available() > 0) {
            IOUtils.copy(cos.getInputStream(), out);
            byte[] responseEntity = out.toByteArray();
            responsePayload.append(new String(responseEntity));
        }

        LOGGER.info("filter() - Content: {}", responsePayload.toString());
        responseContext.getHeaders().add("Content-Length", "500");
    }
}

关于如何调整上述方法以获得我想要的或任何其他不同的指针,有什么建议吗


共 (1) 个答案

  1. # 1 楼答案

    我使用这个answer的帮助部分解决了这个问题。我之所以这样说,部分是因为我能够成功地控制有效负载,但控制代码不是响应状态代码。理想情况下,如果响应长度大于500,并且我修改了消息内容,我希望发送不同的响应状态代码(除了200 OK)。但这是一个足够好的解决方案,我可以在这一点上继续。如果我也知道如何更新状态代码,我会回来更新这个答案

    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.io.CachedOutputStream;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class ResponsePayloadInterceptor extends AbstractPhaseInterceptor<Message> {
        private static final Logger LOGGER = LoggerFactory.getLogger(ResponsePayloadInterceptor.class);
    
        public ResponsePayloadInterceptor() {
            super(Phase.PRE_STREAM);
        }
    
        @Override
        public void handleMessage(Message message) throws Fault {
            LOGGER.info("handleMessage() - Response intercepted");
            try {
                OutputStream outputStream = message.getContent(OutputStream.class);
                CachedOutputStream cachedOutputStream = new CachedOutputStream();
                message.setContent(OutputStream.class, cachedOutputStream);
    
                message.getInterceptorChain().doIntercept(message);
    
                cachedOutputStream.flush();
                cachedOutputStream.close();
    
                CachedOutputStream newCachedOutputStream = (CachedOutputStream) message.getContent(OutputStream.class);
                String currentResponse = IOUtils.toString(newCachedOutputStream.getInputStream(), "UTF-8");
                newCachedOutputStream.flush();
                newCachedOutputStream.close();
    
                if (currentResponse != null) {
                    LOGGER.info("handleMessage() - Response: {}", currentResponse);
                    LOGGER.info("handleMessage() - Response Length: {}", currentResponse.length());
    
                    if (currentResponse.length() > 500) {
                        InputStream replaceInputStream = IOUtils.toInputStream("{\"message\":\"Too much data\"}", "UTF-8");
    
                        IOUtils.copy(replaceInputStream, outputStream);
                        replaceInputStream.close();
    
                        message.setContent(OutputStream.class, outputStream);
                        outputStream.flush();
                        outputStream.close();
                    } else {
                        InputStream replaceInputStream = IOUtils.toInputStream(currentResponse, "UTF-8");
    
                        IOUtils.copy(replaceInputStream, outputStream);
                        replaceInputStream.close();
    
                        message.setContent(OutputStream.class, outputStream);
                        outputStream.flush();
                        outputStream.close();
                    }
                }
            } catch (IOException e) {
                LOGGER.error("handleMessage() - Error", e);
                throw new RuntimeException(e);
            }
        }