在基于 CXF JAX-RS 的服务中限制输出负载响应
Limit output payload response in CXF JAX-RS based service
我有多个 jax-rs 使用 cxf/spring 构建的服务。我想控制所有服务的输出负载响应大小。为简单起见,假设任何服务中 api 的 none 应该 return 一个 JSON 响应负载超过 500 字符,我想在一个地方控制它,而不是依赖单独的服务来满足这个要求。 (我们已经在所有服务所依赖的自定义 framework/base 组件中内置了其他功能)。
我已经尝试使用 JAX-RS 的 WriterInterceptor
、ContainerResponseFilter
和 CXF 的 Phase Interceptor
来实现这个,但是 none 的方法似乎完全满足我的要求。有关我目前所做工作的更多详细信息:
选项 1:(WriterInteceptor) 在覆盖的方法中,我获取输出流并将缓存的最大大小设置为 500。当我调用 api return 响应负载中超过 500 个字符,我得到 HTTP 400
错误请求状态,但响应 body 包含整个 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,我创建一个新的 Response object 只有数据 Too much data 并将其设置在消息中。即使响应超过 500 个字符,我也会得到 HTTP 200
OK 状态和整个 JSON。只有当我将阶段用作 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 much data 创建一个新的输出流并将其设置在消息中。但是,如果响应负载大于 500 个字符,我仍然会得到一个 HTTP 200
OK 状态和一个无效的 JSON 响应(整个 JSON + 附加文本),即响应如下所示:[{"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
响应 header,值为 500。如果响应长度 > 500 ,我得到 HTTP 200
OK 状态和无效的 JSON 响应(截断为 500 个字符)。如果响应长度小于 500,仍会获得 HTTP 200
OK 状态,但客户端等待更多数据被服务器 returned(如预期的那样)并超时,这不是理想的解决方案。
@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");
}
}
关于如何调整上述方法以获得我想要的或任何其他不同的指针有什么建议吗?
我使用 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);
}
}
我有多个 jax-rs 使用 cxf/spring 构建的服务。我想控制所有服务的输出负载响应大小。为简单起见,假设任何服务中 api 的 none 应该 return 一个 JSON 响应负载超过 500 字符,我想在一个地方控制它,而不是依赖单独的服务来满足这个要求。 (我们已经在所有服务所依赖的自定义 framework/base 组件中内置了其他功能)。
我已经尝试使用 JAX-RS 的 WriterInterceptor
、ContainerResponseFilter
和 CXF 的 Phase Interceptor
来实现这个,但是 none 的方法似乎完全满足我的要求。有关我目前所做工作的更多详细信息:
选项 1:(WriterInteceptor) 在覆盖的方法中,我获取输出流并将缓存的最大大小设置为 500。当我调用 api return 响应负载中超过 500 个字符,我得到 HTTP 400
错误请求状态,但响应 body 包含整个 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,我创建一个新的 Response object 只有数据 Too much data 并将其设置在消息中。即使响应超过 500 个字符,我也会得到 HTTP 200
OK 状态和整个 JSON。只有当我将阶段用作 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 much data 创建一个新的输出流并将其设置在消息中。但是,如果响应负载大于 500 个字符,我仍然会得到一个 HTTP 200
OK 状态和一个无效的 JSON 响应(整个 JSON + 附加文本),即响应如下所示:[{"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
响应 header,值为 500。如果响应长度 > 500 ,我得到 HTTP 200
OK 状态和无效的 JSON 响应(截断为 500 个字符)。如果响应长度小于 500,仍会获得 HTTP 200
OK 状态,但客户端等待更多数据被服务器 returned(如预期的那样)并超时,这不是理想的解决方案。
@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");
}
}
关于如何调整上述方法以获得我想要的或任何其他不同的指针有什么建议吗?
我使用 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);
}
}