You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus中使用@RequestScoped自定义请求上下文时字段始终为null的问题求助

Quarkus中使用@RequestScoped自定义请求上下文时字段始终为null的问题求助

问题分析

从你的代码和描述来看,核心问题是JAX-RS过滤器与Quarkus CDI请求上下文的交互导致UserContext实例不共享:

  1. 你用@Provider标注的AuthenticationContextProvider是传统JAX-RS过滤器,默认生命周期为@Singleton。在单例过滤器中注入@RequestScoped的UserContext时,CDI会注入代理对象,但过滤器执行filter方法时,可能还未完全激活CDI请求上下文,导致代理无法正确绑定到当前请求的UserContext实例,最终设置的id无法同步到后续资源类使用的实例中。
  2. 你使用的quarkus-rest是RESTEasy Reactive实现,它和传统JAX-RS过滤器模型存在适配差异,传统的ContainerRequestFilter可能无法完美兼容Quarkus的请求上下文传播逻辑。

下面是几个按优先级排序的可行解决方案:


方案一:直接在UserContext中注入上下文(最简洁)

不需要额外过滤器,直接在UserContext中通过Quarkus的CDI能力注入安全上下文或JWT主体,利用框架内置的上下文传播机制:

@RequestScoped
public class UserContext {

    @Inject
    Optional<JWTCallerPrincipal> jwtPrincipal;

    @Inject
    Optional<SecurityContext> securityContext;

    public String getId() {
        // 优先取JWT主体的name, fallback到普通Principal的name
        return jwtPrincipal.map(JWTCallerPrincipal::getName)
                .orElseGet(() -> securityContext
                        .flatMap(sc -> Optional.ofNullable(sc.getUserPrincipal()))
                        .map(Principal::getName)
                        .orElse(null));
    }
}

这个方案可以直接删掉AuthenticationContextProvider过滤器,在GreetingResource中注入UserContext后调用getId()即可,Quarkus会自动处理请求上下文绑定。


方案二:改用Quarkus REST原生过滤器(适配Reactive生态)

如果想保留过滤器逻辑,建议使用Quarkus REST(RESTEasy Reactive)原生的@ServerRequestFilter注解替代传统JAX-RS过滤器,同时将过滤器设为@RequestScoped,确保每个请求的过滤器实例绑定当前请求的UserContext:

@RequestScoped
@ServerRequestFilter
public class AuthenticationContextProvider {

    @Inject
    UserContext userContext;

    public void filter(ContainerRequestContext requestContext) {
        var securityContext = requestContext.getSecurityContext();

        if (securityContext != null) {
            var principal = securityContext.getUserPrincipal();

            if (principal != null && principal instanceof JWTCallerPrincipal jwt) {
                userContext.setId(jwt.getName());
            } else if (principal != null) {
                userContext.setId(principal.getName());
            }
        }
    }
}

该方案完全适配Quarkus的Reactive生态,请求上下文传播更可靠。


方案三:修复传统JAX-RS过滤器的作用域

如果一定要用传统ContainerRequestFilter,可以将过滤器的作用域改为@RequestScoped,让每个请求对应一个过滤器实例,确保注入的UserContext是当前请求的真实实例:

@RequestScoped
@Provider
public class AuthenticationContextProvider implements ContainerRequestFilter {

    @Inject
    UserContext userContext;

    @Override
    public void filter(ContainerRequestContext requestContext) {
        // 原有逻辑保持不变
    }
}

修改后,过滤器的生命周期变为请求级,设置的id就能在后续资源类中正常获取。


验证建议

你可以在UserContext的setId和getId方法中添加日志,验证实例是否一致:

@RequestScoped
public class UserContext {

    private String id;
    private final Logger log = Logger.getLogger(UserContext.class.getName());

    public String getId() {
        log.info("获取UserContext实例[" + this + "]的id: " + id);
        return id;
    }

    public void setId(String id) {
        this.id = id;
        log.info("设置UserContext实例[" + this + "]的id: " + id);
    }
}

同时在GreetingResource的sayHello方法中也添加日志,查看过滤器和资源类中使用的UserContext实例哈希值是否相同——如果相同,说明上下文已正确传播。

备注:内容来源于stack exchange,提问作者Bad_Pop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:39:31