当前位置: 首页 > 知识库问答 >
问题:

使用WebMvc获取请求头的Spring GraphQL

督翰学
2023-03-14

我有一个Spring GraphQL项目,每个数据提取器(@schemaMap)将从受身份验证保护的远程API获取数据。

我需要将授权头从原始请求(我可以在@QueryMapping方法中看到)传播到数据获取程序。

在数据获取程序中,我可以使用RequestContextHolder获取请求和标题,如下所示:

    val request = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes?)?.getRequest()
    val token = request?.getHeader("authorization")

这工作,但我担心它可能会打破。Spring GraphQL留档指出:

GraphQLJava调用的DataFetcher和其他组件可能并不总是与Spring MVC处理程序在同一线程上执行,例如,如果异步WebInterceptor或DataFetcher切换到不同的线程。

我试图添加一个ThreadLocalAccessor组件,但在调试和阅读源代码的过程中,我发现restoreValue方法只能在WebFlux项目中调用。

我如何确保在WebMvc项目中获得正确的RecestContextHolder

使现代化

我将添加一些代码来更好地解释我的用例。

CurrentActivity是父实体,Booking是子实体。

我需要从后端获取实体,API受身份验证保护。我在原始请求(带有graphql查询的请求)中收到了auth令牌。

CurrentActivityController。kt

@Controller
class CurrentActivityController @Autowired constructor(
    val retrofitApiService: RetrofitApiService,
    val request: HttpServletRequest
) {

    @QueryMapping
    fun currentActivity(graphQLContext: GraphQLContext): CurrentActivity {
        // Get auth token from request.
        // Can I use the injected request here?
        // Or do I need to use Filter + ThreadLocalAccessor to get the token?
        val token = request.getHeader("authorization")
        // Can I save the token to GraphQL Context?
        graphQLContext.put("AUTH_TOKEN", token) 
        return runBlocking {
        // Authenticated API call to backend to get the CurrentActivity
            return@runBlocking entityretrofitApiService.apiHandler.activitiesCurrent(mapOf("authorization" to token))
        }
    }
}

预订管理员。kt

@Controller
class BookingController @Autowired constructor(val retrofitApiService: RetrofitApiService) {

    @SchemaMapping
    fun booking(
        currentActivity: CurrentActivity,
        graphQLContext: GraphQLContext,
    ): Booking? {
        // Can I retrieve the token from GraphQL context?
        val token: String = graphQLContext.get("AUTH_TOKEN")
        return runBlocking {
            // Authenticated API call to backend to get Booking entity
            return@runBlocking currentActivity.currentCarBookingId?.let { currentCarBookingId ->
                retrofitApiService.apiHandler.booking(
                    headerMap = mapOf("authorization" to token),
                    bookingId = currentCarBookingId
                )
            }
        }
    }
}

共有1个答案

吴西岭
2023-03-14

ThreadLocalAccess概念实际上是一种在环境中存储/恢复上下文值的方法,在该环境中,如果没有其他架构体系已经支持,则可以在不同的线程上异步执行。

在SpringWebFlux中,已经存在了反应器上下文并填充了这个角色。WebFlux应用程序应该使用反应DataFetcher和反应器上下文。

ThreadLocalAccessor实现对Spring MVC应用程序最有用。任何ThreadLocalAccessorbean都将由启动程序自动配置。

在您的情况下,您可以遵循其中一个示例并进行类似的安排:

  • 声明一个提取头值的Servlet过滤器,并将其设置为具有知名名称的请求属性
  • 创建一个线程本地访问器组件,并使用它将请求属性存储到上下文中
  • 从您的DataFetcher
  • 中获取相关属性

我尝试添加ThreadLocalAccessor组件,但在调试和阅读源代码的过程中,我发现restoreValue方法只在WebFlux项目中被调用。

请注意,只有当当前Thread不是最初提取的值时,才调用restoreValue(不需要做任何事情,值已经在Threadlocal中)。

我已经成功地测试了这种方法,从RequestContextHolder获取“authorization”HTTP头值。您似乎尝试过这种方法,但没有成功——您能否尝试使用1.0.0-M3,如果不起作用,请告知我们?您可以在项目上创建一个问题,并链接到复制该问题的示例项目。

如果不想处理ThreadLocal绑定的值,可以始终使用WebInterceptor使用自定义值扩充GraphQLContext

下面是一个例子:

java prettyprint-override">@Component
public class AuthorizationWebInterceptor implements WebInterceptor {
    @Override
    public Mono<WebOutput> intercept(WebInput webInput, WebInterceptorChain chain) {
        String authorization = webInput.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        webInput.configureExecutionInput((input, inputBuilder) ->
                inputBuilder
                        .graphQLContext(contextBuilder -> contextBuilder.put("Authorization", authorization))
                        .build()
        );
        return chain.next(webInput);
    }
}

这样,您就可以从GraphQL上下文中获取该值:

    @QueryMapping
    public String greeting(GraphQLContext context) {
        String authorization = context.getOrDefault("Authorization", "default");
        return "Hello, " + authorization;
    }
 类似资料:
  • 获取当前请求的所有HTTP 请求头信息,如: $headers = $request->header(); print_r($headers); 直接获取某个请求头信息,如: $token = $request->header('XX-Token'); HTTP请求头信息的名称不区分大小写,并且下划线会自动转换为-,所以下面的写法都是等效的: $token = $request->head

  • 获取当前请求的所有HTTP 请求头信息,如: $headers = $request->header(); print_r($headers); 直接获取某个请求头信息,如: $token = $request->header('XX-Token'); HTTP请求头信息的名称不区分大小写,并且下划线会自动转换为-,所以下面的写法都是等效的: $token = $request->head

  • 这是我的AngularJS代码(如果我删除header选项,它可以正常工作)。 请求如下: 答复: 我添加了和头,但请求仍然失败 大写字母(我的意思是vs)是否会导致失败?如果是,我如何才能让AngularJS停止这样做? Go服务器路由代码:

  • 问题内容: 我正在尝试教自己一些基本的网络抓取。使用Python的请求模块,在尝试以下操作之前,我能够抓取各种网站的html: 我得到的不是基本的html,而是本页面的内容: 我已经尝试过将get / post与我可以从文档,SO和其他示例中猜到的每种语法进行多种组合。我不明白上面看到的内容,无法将其转换为可以阅读的任何内容,也无法弄清楚如何获得自己真正想要的东西。我的问题是,如何获取以上页面的h

  • 服务工作者内部的fetch事件似乎没有接收到请求头,尽管MDN文档中有说明: 通过调用FetchEvent返回的request对象的参数,可以检索关于每个请求的大量信息: 事件要求url 事件。要求方法 事件。要求标题 事件。要求身体 从主线程获取资源的代码: 在SW文件中获取事件处理程序: 记录每个fetch事件的标题会给我一个空对象: 标题{} 这阻止了我缓存这个只需要这两个头的特定请求。不需