我正在使用 graphql-java 20.0(在 Kotlin 中的 spring-boot-starter-graphql 中),并且想要将自定义标头添加到我的解析器的响应中。目前,我的解析器仅返回应包含在 graphql 响应中的实体(类型为
MyEntity
) - 有效:
@QueryMapping
fun myResolver(
@Argument arg1: String,
@Argument arg2: MyInput,
): MyEntity = service.getMyEntity(arg1, arg2)
我尝试按照这个SO答案中的建议将其更改为ResponseEntity,但这显然不是由graphql-java处理的,导致我的响应数据为null:
/* this does not work, since graphql-java doe not map the ResponseEntity's body
and results in a graphql response with null in the data object */
@QueryMapping
fun myResolver(
@Argument arg1: String,
@Argument arg2: MyInput,
): ResponseEntity<MyEntity> = ResponseEntity.ok().run {
this.header("X-My-Header", "whatever")
}.body(service.getMyEntity(arg1, arg2))
但是,我找不到任何替代方案可以让我在响应旁边设置自定义标头。在 StackOverflow 上,我只找到了 Laravel 和 Astro 的答案。在 graphql-spring-boot 仓库中,有一个“类似的问题”近两年来一直没有得到解答。 有人知道在我的 graphql 解析器中设置自定义标头的方法吗?我需要这样做,因为我的标头需要根据某些请求属性而有所不同。
如果有人找到更干净的解决方案,请告诉我。但就目前而言,这是可行的:
object GraphQLMyHeaderThreadLocalStorage {
private val context = ThreadLocal<String>()
var value: String?
get() = context.get()
set(value) = value?.let { context.set(it) } ?: context.remove()
fun clear() = context.remove()
}
在我的解析器中,我现在可以使用我的请求特定值设置此 ThreadLocal:
@QueryMapping
fun myResolver(
@Argument arg1: String,
@Argument arg2: MyInput,
): MyEntity = service.getMyEntity(arg1, arg2).also {
GraphQLMyHeaderThreadLocalStorage.value = "whatever inferred from ${it}"
}
如果我提前包装并在
Filter
之后进行修改,我仍然可以在chain.doFilter()
中修改我的回复:
class GraphQLMyHeaderFilter : Filter {
@Throws(IOException::class, ServletException::class)
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
if (!response.isCommitted) {
val responseWrapper = object : HttpServletResponseWrapper(response as HttpServletResponse) {
fun updateMyHeader(value: String?) {
if (value != null) {
setHeader("X-My-Header", value)
} else {
setHeader("X-My-Header", "default value")
}
}
}
chain.doFilter(request, responseWrapper)
// modify the response after the resolver was called
if (!response.isCommitted) {
val headerValue = try {
GraphQLMyHeaderThreadLocalStorage.value
} finally {
GraphQLMyHeaderThreadLocalStorage.clear()
}
responseWrapper.updateCacheControl(headerValue)
}
} else {
chain.doFilter(request, response)
}
}
}
@Configuration
class FilterConfig {
@Bean
fun graphQLMyHeaderFilter(): FilterRegistrationBean<GraphQLMyHeaderFilter> {
val registrationBean = FilterRegistrationBean<GraphQLMyHeaderFilter>()
registrationBean.filter = GraphQLMyHeaderFilter()
registrationBean.addUrlPatterns("/graphql")
return registrationBean
}
}
备注:
response.isCommitted
我将过滤器限制为仅
FilterConfig
"/*"
模式代替
"/graphql"
,或者删除
FilterConfig
并用
GraphQLMyHeaderFilter
注释
@Component
。
确保之后始终
GraphQLMyHeaderThreadLocalStorage.clear()
Filter
ResponseBodyAdvice
。
HandlerInterceptor
已被访问,但
HandlerInterceptor.preHandle()
在解析器之前执行(两次偶数
)并且
HandlerInterceptor.postHandle()
收到已提交的响应(即,无法再修改响应)。