簡體   English   中英

限制基於CXF JAX-RS的服務中的輸出有效負載響應

[英]Limit output payload response in CXF JAX-RS based service

我有多個使用cxf / spring構建的jax-rs服務。 我想控制所有服務的輸出有效負載響應大小。 為了簡單起見,假設任何服務中的api都不應該返回超過500個字符的JSON響應有效負載,我想在一個地方進行控制,而不是依賴單個服務來滿足此要求。 (我們已經在所有服務所依賴的自定義框架/基本組件中內置了其他功能)。

我嘗試使用JAX-RS的WriterInterceptorContainerResponseFilter和CXF的Phase Interceptor來實現此WriterInterceptor ,但是似乎沒有一種方法可以完全滿足我的要求。 到目前為止我所做的更多詳細信息:

選項1:(WriterInteceptor)在重寫的方法中,我獲得輸出流並將緩存的最大大小設置為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相位接收器)在重寫方法中,我從輸出流中獲取響應作為String並檢查其大小。 如果大於500,我將創建一個新的Response對象,其中僅包含數據Too many data並將其設置在消息中。 即使響應> 500個字符,我也會獲得整個JSON的HTTP 200 OK狀態。 只有當我將該階段用作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,我將創建一個新的輸出流,其字符串為Too too data ,並將其設置為message。 但是,如果響應的有效載荷大於500個字符,我仍然會收到HTTP 200 OK狀態,其中包含無效的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,我添加了一個Content-Length響應標頭,其值為500。如果響應長度> 500,我將獲得HTTP 200 OK狀態以及無效的JSON響應(截斷為500個字符)。 如果響應長度小於500,則仍將顯示HTTP 200 OK狀態,但是客戶端將等待服務器返回更多數據(如預期)並超時,這不是理想的解決方案。

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

關於如何調整上述方法以獲得所需的任何建議或任何其他不同的指示?

我使用此答案的幫助部分解決了此問題 我之所以這么說是部分原因是因為我能夠成功控制有效負載,但不能控制響應狀態代碼。 理想情況下,如果響應長度大於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);
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM