在quarkus-resteasy-reactive中如何在服务响应过滤器中记录请求体
Quarkus ServerResponseFilter 记录请求体正确实现方案
错误原因
你当前遇到的类转换异常,是因为Quarkus Resteasy Reactive在响应过滤器中注入的ContainerRequestContext实际是PreventAbortResteasyReactiveContainerRequestContext包装类,和QuarkusResteasyReactiveRequestContext无继承关系,强转自然会失败。同时请求体默认仅可被消费一次,若未提前缓存,响应阶段也可能无法读取到请求体内容。
解决方案
方案1:直接注入QuarkusResteasyReactiveRequestContext(最简实现)
不需要从ContainerRequestContext强转,直接将QuarkusResteasyReactiveRequestContext作为响应过滤器方法的入参,Quarkus会自动注入正确的上下文实例:
import io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext; import io.smallrye.mutiny.Uni; import org.jboss.resteasy.reactive.server.ServerResponseFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; public class CustomResponseFilter { private static final Logger logger = LoggerFactory.getLogger(CustomResponseFilter.class); @ServerResponseFilter public Uni<Void> filter(ContainerRequestContext request, ContainerResponseContext response, QuarkusResteasyReactiveRequestContext reactiveCtx) { logger.info("请求体内容:{}", reactiveCtx.getRequestEntity()); return Uni.createFrom().nullItem(); } }
注意:该方法拿到的是已经完成反序列化的请求体对象,如果你需要原始字符串格式的请求体,使用方案2
方案2:搭配请求过滤器缓存原始请求体
如果需要获取未反序列化的原始请求体字符串,需要先在请求过滤器中缓存请求体流副本,避免流被消费后业务逻辑无法读取:
- 先实现请求过滤器缓存请求体
import org.jboss.resteasy.reactive.server.ServerRequestFilter; import org.apache.commons.io.IOUtils; import java.nio.charset.StandardCharsets; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.ext.Provider; import java.io.ByteArrayInputStream; import java.io.IOException; @Provider public class CustomRequestFilter { @ServerRequestFilter public void preFilter(ContainerRequestContext requestContext) throws IOException { // 读取原始请求体 String requestBody = IOUtils.toString(requestContext.getEntityStream(), StandardCharsets.UTF_8); // 把请求体存入请求属性 requestContext.setProperty("rawRequestBody", requestBody); // 把请求体流写回去,不影响后续业务逻辑读取 requestContext.setEntityStream(new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8))); } }
- 响应过滤器直接读取缓存的请求体
import io.smallrye.mutiny.Uni; import org.jboss.resteasy.reactive.server.ServerResponseFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; public class CustomResponseFilter { private static final Logger logger = LoggerFactory.getLogger(CustomResponseFilter.class); @ServerResponseFilter public Uni<Void> filter(ContainerRequestContext request, ContainerResponseContext response) { String requestBody = (String) request.getProperty("rawRequestBody"); if (requestBody != null) { logger.info("原始请求体:{}", requestBody); } return Uni.createFrom().nullItem(); } }
注意事项
- 大体积请求体(如文件上传)不要全量缓存/打印,避免触发OOM
- 如果项目配置了请求体读取阈值,超过阈值的请求体不会自动存入
getRequestEntity(),需要自行处理流的缓存 - 若存在请求体加密、压缩的场景,需要先解密/解压后再记录
内容的提问来源于stack exchange,提问作者AmsterdamLuis
相关产品推荐
相关产品推荐

