如何在Quarkus中实现类似Spring ResponseBodyAdvice的自定义响应封装?
在Quarkus中实现全局响应封装(类似Spring ResponseBodyAdvice)
Quarkus中没有直接对应Spring ResponseBodyAdvice的组件,但可以通过JAX-RS标准的ContainerResponseFilter实现全局统一响应封装,效果和你在Spring中的实现一致,同时能覆盖成功响应的包装逻辑。以下是具体实现步骤:
1. 定义统一响应体结构
先对齐Spring代码中的Response和ResponseUtil,统一响应格式:
public class ApiResponse<T> { private int code; private String message; private T data; private PageMeta meta; // 分页元数据,按需使用 // 构造方法、getter/setter省略 } public class PageMeta { private long totalElements; private int totalPages; private int currentPage; private int pageSize; // 构造方法、getter/setter省略 } public class ResponseUtil { // 普通响应构建 public static <T> ApiResponse<T> build(T data) { ApiResponse<T> response = new ApiResponse<>(); response.setCode(200); response.setMessage("success"); response.setData(data); return response; } // 分页响应构建 public static <T> ApiResponse<T> buildWithMeta(Page<T> page) { ApiResponse<T> response = build(page.getContent()); PageMeta meta = new PageMeta(); meta.setTotalElements(page.getTotalElements()); meta.setTotalPages(page.getTotalPages()); meta.setCurrentPage(page.getNumber() + 1); // Quarkus Page索引从0开始,转成前端常用的从1计数 meta.setPageSize(page.getSize()); response.setMeta(meta); return response; } }
2. 实现全局响应过滤器
通过ContainerResponseFilter拦截所有响应,复刻Spring中的逻辑:
import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.container.ContainerResponseFilter; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.ext.Provider; import jakarta.json.bind.JsonbBuilder; import java.util.Arrays; import java.util.List; @Provider @Priority(1000) // 设置优先级,确保在Quarkus默认过滤器前执行 public class ResponseWrapperFilter implements ContainerResponseFilter { private static final List<String> IGNORED_URIS = Arrays.asList("swagger", "v2/api-docs", "v3/api-docs", "actuator"); @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { String path = requestContext.getUriInfo().getPath(); Object body = responseContext.getEntity(); // 跳过特定URI或已封装的响应 if (IGNORED_URIS.stream().anyMatch(path::contains) || body instanceof ApiResponse) { return; } // 处理分页响应 if (body instanceof Page<?>) { responseContext.setEntity(ResponseUtil.buildWithMeta((Page<?>) body)); } // 处理String类型:手动序列化为JSON,避免返回纯文本 else if (body instanceof String) { ApiResponse<String> wrapped = ResponseUtil.build((String) body); String json = JsonbBuilder.create().toJson(wrapped); responseContext.setEntity(json); responseContext.getHeaders().putSingle("Content-Type", MediaType.APPLICATION_JSON); } // 处理其他普通响应 else { responseContext.setEntity(ResponseUtil.build(body)); } } }
3. 关键注意事项
@Priority注解:数值越小优先级越高,设置为1000能确保过滤器在Quarkus默认的响应处理逻辑前执行。- String类型特殊处理:JAX-RS对String的默认行为是直接返回文本,所以需要手动转成JSON并修改响应头,保证前端拿到标准JSON格式。
- 忽略URI逻辑:和Spring实现一致,跳过Swagger、Actuator等不需要封装的接口。
补充:WriterInterceptor方案(序列化阶段处理)
如果需要在响应体序列化前做细粒度控制,可使用WriterInterceptor,适合聚焦序列化环节的场景:
import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.ext.WriterInterceptor; import jakarta.ws.rs.ext.WriterInterceptorContext; import java.io.IOException; @Provider public class ResponseWriterInterceptor implements WriterInterceptor { @Override public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { Object body = context.getEntity(); if (!(body instanceof ApiResponse)) { ApiResponse<?> wrapped = ResponseUtil.build(body); context.setEntity(wrapped); } context.proceed(); } }
不过ContainerResponseFilter更适合全局响应封装场景,能更早拦截响应,处理逻辑更全面。
内容的提问来源于stack exchange,提问作者Mete Alpaslan KATIRCIOGLU
相关产品推荐
相关产品推荐

