繁体   English   中英

在 Quarkus 中将反应式 @ServerRequestFilter 与 RestClient 一起使用

[英]Using reactive @ServerRequestFilter with RestClient in Quarkus

dev-mode被激活时,我想在我的请求中添加一些自定义身份验证标头。 这应该使我的开发更容易,因为我不必手动将它们添加到自己身上。

我发现的是方法注释ServerRequestFilter ,它在达到 controller 级别之前拦截正在进行的请求。 带注释的 function 提供了ContainerRequestContext参数,我可以在其中轻松添加我的标题。

现在的问题:要知道自定义标头值,我必须对 API 进行外部 rest 调用(我为此使用了RestClient )。 由于我使用的是响应式库,因此我得到了异常org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException因为这个调用。

由于我使用的是 Kotlin,因此我试图将我的方法标记为可暂停。 But this lead to a build error Method 'preCall$suspendImpl of class 'com.kaz.request.service.RequestInterceptor' cannot be static as it is annotated with '@org.jboss.resteasy.reactive.server.ServerRequestFilter'

这是我的代码:

@ApplicationScoped
class RequestInterceptor @Inject constructor(val authService: AuthService) {

    @ServerRequestFilter
    suspend fun preCall(requestContext: ContainerRequestContext) {
        validateIdTokenHeader(requestContext)
    }

    private suspend fun validateIdTokenHeader(requestContext: ContainerRequestContext) {
        val isTokenHeaderAbsent = requestContext.getHeaderString(Headers.X_ID_TOKEN) == null
        val isDevModeEnabled = LaunchMode.current() == LaunchMode.DEVELOPMENT

        if (isTokenHeaderAbsent && !isDevModeEnabled) {
            throw AuthExceptions.ID_TOKEN_IS_ABSENT
        } else {
            injectDevUserIdToken(requestContext)
        }
    }

    private suspend fun injectDevUserIdToken(requestContext: ContainerRequestContext) {
        // This call is making the request and block
        val idToken = authService.getIdToken("someHash")
        requestContext.headers.putSingle(Headers.X_ID_TOKEN, idToken)
    }
}

我还尝试在我的 RestClient 中使用Mutiny 我订阅了 Uni 并在结果可用时添加了 header。 但后来我遇到了问题,在 header 可以添加到请求之前,我的控制器/端点已经被调用。

端点可能如下所示:

    @Path("hello/{id}")
    @GET
    suspend fun get(
        //This header is what I want to add automatically, when dev mode is active.
        @RestHeader(Headers.X_ID_TOKEN) idToken: UserIdToken,
        @RestPath id: UUID,
        @Context httpRequest: HttpServerRequest
    ): Response {
        val request = RequestDTO(id, excludeFields, idToken.userId)
        val envelope = service.findById(request)

        return ResponseBuilder.build(httpRequest, envelope)
    }

您的实现中的某些东西阻止了 IO 所以你可以尝试找到它并用Uni包装它,或者你可以用@Blocking注解标记方法,然后过滤器将在工作线程上运行。

如何创建一个延迟 Uni 并将其发送到工作池?

   val idToken = Uni.createFrom().item { authService.getIdToken("someHash") }
            .runSubscriptionOn(Infrastructure.getDefaultWorkerPool())

或使用默认的 io 协程调度程序调用阻塞代码

withContext(Dispatchers.IO){
        authService.getIdToken("someHash")
}

最后,也许使用 Vertx.executeBlocking 将是最简单的方法

vertx.executeBlocking(Uni.createFrom().item { 
     authService.getIdToken("someHash")
})

有时不仅要阅读在线文档页面,还要阅读相应 class 的内联注释。

ServerRequestFilter.java说:

The return type of the method must be either be of type void, Response, RestResponse, Optional<Response>, Optional<RestResponse>, Uni<Void>, Uni<Response> or Uni<RestResponse>. 

...

Uni<Void> should be used when filtering needs to perform a
non-blocking operation but the filter cannot abort processing. Note
that Uni<Void> can easily be produced using: Uni.createFrom().nullItem()

所以入口点 function 也必须返回一个 Uni,而不仅仅是 RestClient。 像下面这样改变它就足够了

@ServerRequestFilter
fun preCall(requestContext: ContainerRequestContext) : Uni<Void> {
    //Here I return a Uni<Void> back using Uni.createFrom().nullItem()
    return validateIdTokenHeader(requestContext)
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM