我使用cxf/spring构建了多个jax-rs服务。我想控制所有服务的输出有效负载响应大小。为了简单起见,假设任何服务中的api都不应该返回超过500个字符的json响应负载,我想在一个地方控制它,而不是依赖单个服务来满足这个要求(我们已经在所有服务所依赖的自定义框架/基本组件中内置了其他特性。
我尝试过用jax-rs实现这个 WriterInterceptor
, ContainerResponseFilter
和cxf的 Phase Interceptor
,但这些方法似乎都不能完全满足我的要求。关于我目前所做工作的更多细节:
选项1:(writerneceptor)在重写的方法中,我获取输出流并将缓存的最大大小设置为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)在重写的方法中,我从输出流中获取字符串形式的响应,并检查其大小。如果大于500,我将创建一个新的响应对象,只包含过多的数据,并在消息中设置它。即使响应大于500个字符,我也会得到一个 HTTP 200
整个json的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 phase interceptor)同上,但只更改if块的内容。如果响应长度大于500,我将创建一个新的输出流,其中字符串的数据太多,并在消息中设置它。但是如果响应负载大于500个字符,我仍然会得到一个 HTTP 200
具有无效json响应(整个json+附加文本)的ok状态,即响应如下所示: [{"data":"", ...}, {...}]Too much 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
正常状态,但客户机等待服务器返回更多数据(如预期的那样)并超时,这不是理想的解决方案。
@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条答案
按热度按时间5tmbdcev1#
我通过这个答案的帮助部分地解决了这个问题。我之所以这么说,部分是因为我成功地控制了有效负载,但不是响应状态码。理想情况下,如果响应长度大于500并且我修改了消息内容,我希望发送一个不同的响应状态代码(除了200ok)。但这是一个足够好的解决方案,我可以在这一点上继续。如果我也知道如何更新状态码,我会回来更新这个答案。