79340317

Date: 2025-01-08 18:11:51
Score: 2
Natty:
Report link

If someone find's a cleaner solution, please let me know. But for now, this works:

I created a ThreadLocal to hold the header value:

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()
}

In my resolver, I can now set this ThreadLocal with my request-specific value:

@QueryMapping
fun myResolver(
    @Argument arg1: String,
    @Argument arg2: MyInput,
): MyEntity = service.getMyEntity(arg1, arg2).also {
    GraphQLMyHeaderThreadLocalStorage.value = "whatever inferred from ${it}"
}

And I can still modify my response in a Filter if I wrap it in advance and do the modification after 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
    }
}

Notes:

Reasons:
  • RegEx Blacklisted phrase (2.5): please let me know
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: Valentin Kuhn