我有一個 Spring GraphQL 專案。每個資料獲取器 ( @SchemaMapping) 將從受身份驗證保護的遠程 API 獲取資料。
我需要將授權標頭從原始請求(我可以在@QueryMapping方法內部看到)傳播到資料獲取器。
在資料提取器中,我可以使用 RequestContextHolder 來獲取請求和這樣的標頭:
val request = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes?)?.getRequest()
val token = request?.getHeader("authorization")
這有效,但我擔心它會破裂。Spring GraphQL檔案指出:
GraphQL Java 呼叫的 DataFetcher 和其他組件可能并不總是在與 Spring MVC 處理程式相同的執行緒上執行,例如,如果異步 WebInterceptor 或 DataFetcher 切換到不同的執行緒。
我嘗試添加一個ThreadLocalAccessor組件,但在我看來,通過除錯和閱讀源代碼,該restoreValue方法僅在 WebFlux 專案中被呼叫。
我怎樣才能確保RequestContextHolder在 WebMvc 專案中獲得正確的結果?
更新
我將添加一些代碼以更好地解釋我的用例。
CurrentActivity是父物體,而Booking是子物體。
我需要使用受身份驗證保護的 API 從后端獲取物體。我在原始請求中收到身份驗證令牌(帶有 graphql 查詢的那個)。
當前活動控制器.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
)
}
}
}
}
uj5u.com熱心網友回復:
這個ThreadLocalAccessor概念實際上是一種在可以異步執行的環境中存盤/恢復背景關系值的方法,如果沒有其他基礎設施已經支持,則在不同的執行緒上。
在 Spring WebFlux 的情況下,Reactor 背景關系已經存在并填補了這個角色。WebFlux 應用程式應該使用 reactDataFetchers和Reactor Context 本機。
ThreadLocalAccessor實作對 Spring MVC 應用程式最有用。任何ThreadLocalAccessorbean 都將由 starter 自動配置。
在您的情況下,您可以按照其中一個示例進行操作并進行類似的安排:
- 宣告一個Servlet 過濾器,用于提取標頭值并將其設定為具有眾所周知名稱的請求屬性
- 創建一個ThreadLocalAccessor 組件并使用它來將請求屬性存盤到背景關系中
- 從您的
DataFetcher
我嘗試添加一個 ThreadLocalAccessor 組件,但在我看來,通過除錯和閱讀源代碼,restoreValue 方法僅在 WebFlux 專案中被呼叫。
請注意,restoreValue僅當當前執行緒不是從最初提取的值時才呼叫 (無需執行任何操作,值已在 中ThreadLocal)。
我已經成功測驗了這種方法,從RequestContextHolder. 看來您嘗試過這種方法沒有成功 - 您可以嘗試使用 1.0.0-M3 并讓我們知道它是否不起作用嗎?您可以使用指向重現問題的示例專案的鏈接在專案上創建問題。
替代解決方案
如果您不想處理ThreadLocal系結值,您始終可以使用 aWebInterceptor來增加GraphQLContext自定義值。
下面是一個例子:
@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;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/353330.html
標籤:弹簧靴 弹簧 mvc 图形 spring-graphql
