如何在Quarkus Amazon Lambda HTTP扩展中装饰所有RESTEasy Reactive请求处理?
在AWS Lambda上的Quarkus RESTEasy Reactive应用中装饰所有HTTP请求(含异常场景)
由于RESTEasy Reactive的ContainerResponseFilter在资源抛出异常时不会执行,而Servlet过滤器在Lambda部署场景下不适用,推荐以下两种方案,其中Vert.x全局路由过滤器是最贴合需求的选择:
方案一:使用Vert.x全局路由过滤器(推荐)
Quarkus Amazon Lambda HTTP扩展底层基于Vert.x Web实现,通过@RouteFilter可以定义全局过滤器,覆盖所有请求的处理生命周期——包括请求开始、正常结束、异常终止的场景。
实现代码
import io.quarkus.vertx.web.RouteFilter; import io.vertx.ext.web.RoutingContext; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class GlobalRequestTracker { // 优先级数值越小,过滤器执行越早 @RouteFilter(100) public void trackRequest(RoutingContext ctx) { // 记录请求开始时间 long startTime = System.currentTimeMillis(); String requestPath = ctx.request().path(); // 继续执行后续路由逻辑,注册完成回调(无论成功/失败都会触发) ctx.next(event -> { // 请求完成后的统一处理逻辑 long duration = System.currentTimeMillis() - startTime; int statusCode = ctx.response().getStatusCode(); // 示例:打印请求完成日志 System.out.printf("请求 [%s] 处理完成,状态码: %d,耗时: %dms%n", requestPath, statusCode, duration); // 处理异常场景 if (event.failed()) { Throwable exception = event.cause(); System.err.printf("请求 [%s] 发生异常: %s%n", requestPath, exception.getMessage()); } }); } }
优势
- 覆盖所有进入应用的HTTP请求,包括RESTEasy路由、静态资源等
- 不受RESTEasy异常处理流程影响,异常场景下仍能触发完成回调
- 与Quarkus Lambda HTTP扩展的底层实现完全兼容
方案二:CDI拦截器(仅覆盖标注的REST资源)
如果只需要装饰REST资源方法的处理逻辑,可以使用CDI拦截器配合@AroundInvoke,通过try-finally块确保无论成功还是异常都能执行收尾逻辑。
步骤1:定义拦截器绑定注解
import jakarta.interceptor.InterceptorBinding; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @InterceptorBinding @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface TrackRequest { }
步骤2:实现拦截器
import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; import java.util.logging.Logger; @Interceptor @TrackRequest public class RequestTrackingInterceptor { private static final Logger LOG = Logger.getLogger(RequestTrackingInterceptor.class.getName()); @AroundInvoke public Object track(InvocationContext ctx) throws Exception { long startTime = System.currentTimeMillis(); String resourceMethod = ctx.getMethod().getDeclaringClass().getSimpleName() + "." + ctx.getMethod().getName(); try { // 执行目标资源方法 return ctx.proceed(); } catch (Exception e) { // 记录异常信息 LOG.severe("资源方法 [" + resourceMethod + "] 处理异常: " + e.getMessage()); throw e; } finally { // 无论成功/失败都执行的收尾逻辑 long duration = System.currentTimeMillis() - startTime; LOG.info("资源方法 [" + resourceMethod + "] 处理完成,耗时: " + duration + "ms"); } } }
步骤3:在REST资源上标注
import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/api") @TrackRequest public class ExampleResource { @GET @Path("/hello") public String hello() { return "Hello World"; } }
局限性
- 仅能拦截标注了
@TrackRequest的资源方法,无法覆盖所有HTTP请求 - 对异步资源方法的拦截需要额外注意上下文传递
内容的提问来源于stack exchange,提问作者jdgilday
相关产品推荐
相关产品推荐

