您如何使用 graphql-kotlin 在 Spring 引导服务器中添加 CORS header?

How do you handle adding a CORS header in a Spring Boot server with graphql-kotlin?

我正在使用由 Expedia 创建的 GraphQL Kotlin library & open-sourced 构建一个简单的 Spring 服务器项目。我有一个后端和 运行,与数据存储对话,我可以通过 Playground 发送查询来获取数据。

当我尝试从我的 React 前端(使用 Apollo)连接时,由于 CORS 问题,初始 OPTIONS 请求收到 404 响应。我对 Spring Boot 比较陌生,SO 和其他地方记录了很多潜在的方法。

如何拦截响应并添加适当的 Access-Control-Allow-Origin header?

在尝试了上述许多方法之后,我找到了一种适用于我的特定变量组合的方法。

例如,这些无效:

  1. 覆盖 addCorsMappings 的 WebFluxConfigurer 组件 函数
  2. @CrossOrigin 查询函数注释
  3. 将 spring 引导执行器库添加到项目并将 management.endpoints.web.cors 配置添加到 application.yml,根据 the Spring documentation

根据这个 SO 问题,最终对我有用的是自定义 WebFilter 子类:

还有 this tutorial,这说明了很多 Spring Boot 的一般工作方式。

仅在 spring boot-kotlin 服务器中添加下一个配置与 React 和 Apollo 一起工作:

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.web.cors.reactive.CorsUtils
import org.springframework.web.server.WebFilter
import reactor.core.publisher.Mono

@Configuration
class ReactiveConfigurations {

    companion object {
        const val ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN"
        const val ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS"
        const val ALLOWED_ORIGIN = "*"
        const val MAX_AGE = "3600"
    }

    @Bean
    fun corsFilter(): WebFilter {
        return WebFilter { ctx, chain ->
            val request = ctx.request
            if (CorsUtils.isCorsRequest(request)) {
                val response = ctx.response
                val headers = response.headers
                headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN)
                headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS)
                headers.add("Access-Control-Max-Age", MAX_AGE)
                headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS)
                if (request.method === HttpMethod.OPTIONS) {
                    response.statusCode = HttpStatus.OK
                    return@WebFilter Mono.empty<Void>()
                }
            }
            chain.filter(ctx)
        }
    }
}

你可以这样做:

 @Bean
    @Profile("!production")
    fun corsFilter(): CorsWebFilter = CorsWebFilter(
        UrlBasedCorsConfigurationSource().apply {
            registerCorsConfiguration(
                "/**",
                CorsConfiguration().apply {
                    allowCredentials = true
                    allowedOrigins = listOf("*")
                    allowedHeaders = listOf("*")
                    allowedMethods = listOf("*")
                }
            )
        }
    )

对我来说(使用与您相同的技术),创建自定义过滤器组件 (CorsFilter) 在所有其他选项中效果最好:

我的配置示例:

@Component
class CorsFilter: WebFilter {

    @Value("${cors.allowed_origin}")
    lateinit var allowedOrigin: String

    override fun filter(ctx: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
        ctx.response.headers.add("Access-Control-Allow-Origin", allowedOrigin)
        ctx.response.headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
        ctx.response.headers.add("Access-Control-Allow-Credentials", "true")
        ctx.response.headers.add("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range, Authorization")
        return when {
            ctx.request.method == HttpMethod.OPTIONS -> {
                ctx.response.headers.add("Access-Control-Max-Age", "1728000")
                ctx.response.statusCode = HttpStatus.NO_CONTENT
                Mono.empty()
            }
            else -> {
                ctx.response.headers.add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
                chain.filter(ctx)
            }
        }
    }
}

注意:allowedOrigin 变量取自 applications.properties 个文件