Skip to content

SetResponseHeader GatewayFilter 工厂

概述

SetResponseHeader 是 Spring Cloud Gateway 中的一个重要的网关过滤器工厂,用于设置或替换 HTTP 响应头。这个过滤器在微服务架构中非常有用,特别是当我们需要在网关层统一处理响应头信息时。

业务场景

在实际业务中,SetResponseHeader 过滤器主要解决以下问题:

  • 统一响应头管理:在网关层统一设置安全相关的响应头
  • 版本控制:为不同版本的 API 设置特定的响应头标识
  • 调试信息:添加调试相关的响应头,如请求追踪 ID
  • CORS 配置:设置跨域相关的响应头
  • 缓存控制:统一设置缓存相关的响应头

工作原理

配置语法

SetResponseHeader 过滤器接受两个参数:

  • name:响应头的名称
  • value:响应头的值

IMPORTANT

SetResponseHeader 会替换(而不是添加)所有具有给定名称的响应头。

基本配置示例

YAML 配置方式

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: setresponseheader_route
          uri: https://example.org
          filters:
            - SetResponseHeader=X-Response-Red, Blue

Kotlin DSL 配置方式

kotlin
import org.springframework.cloud.gateway.route.RouteLocator
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class GatewayConfig {

    @Bean
    fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("setresponseheader_route") { r ->
                r.path("/api/**")
                    .filters { f ->
                        f.setResponseHeader("X-Response-Red", "Blue")
                    }
                    .uri("https://example.org")
            }
            .build()
    }
}
java
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("setresponseheader_route", r ->
                r.path("/api/**")
                    .filters(f ->
                        f.setResponseHeader("X-Response-Red", "Blue"))
                    .uri("https://example.org"))
            .build();
    }
}

实际业务示例

1. 设置 API 版本响应头

kotlin
@Configuration
class ApiVersionGatewayConfig {

    @Bean
    fun apiVersionRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            // V1 API 路由
            .route("api_v1") { r ->
                r.path("/api/v1/**")
                    .filters { f ->
                        f.setResponseHeader("X-API-Version", "1.0")
                         .setResponseHeader("X-Service-Name", "user-service")
                    }
                    .uri("http://user-service-v1")
            }
            // V2 API 路由
            .route("api_v2") { r ->
                r.path("/api/v2/**")
                    .filters { f ->
                        f.setResponseHeader("X-API-Version", "2.0")
                         .setResponseHeader("X-Service-Name", "user-service")
                    }
                    .uri("http://user-service-v2")
            }
            .build()
    }
}

2. 设置安全响应头

kotlin
@Configuration
class SecurityHeadersConfig {

    @Bean
    fun securityRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("secure_api") { r ->
                r.path("/secure/**")
                    .filters { f ->
                        f.setResponseHeader("X-Content-Type-Options", "nosniff")
                         .setResponseHeader("X-Frame-Options", "DENY")
                         .setResponseHeader("X-XSS-Protection", "1; mode=block")
                         .setResponseHeader("Strict-Transport-Security", "max-age=31536000")
                    }
                    .uri("http://secure-service")
            }
            .build()
    }
}

URI 变量支持

SetResponseHeader 支持 URI 变量,可以在运行时动态扩展值。

配置示例

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: setresponseheader_route
          uri: https://example.org
          predicates:
            - Host={segment}.myhost.org
          filters:
            - SetResponseHeader=foo, bar-{segment}

Kotlin 实现

kotlin
@Configuration
class DynamicHeaderConfig {

    @Bean
    fun dynamicHeaderRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("dynamic_header") { r ->
                r.host("{tenant}.api.example.com")
                    .filters { f ->
                        // 使用 URI 变量动态设置响应头
                        f.setResponseHeader("X-Tenant-ID", "tenant-{tenant}")
                         .setResponseHeader("X-Request-Path", "path-{path}")
                    }
                    .uri("http://multi-tenant-service")
            }
            .build()
    }
}

高级用法

条件性设置响应头

kotlin
@Component
class ConditionalResponseHeaderFilter : GlobalFilter, Ordered {

    override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
        return chain.filter(exchange).then(
            Mono.fromRunnable {
                val response = exchange.response
                val request = exchange.request

                // 根据请求条件设置响应头
                when {
                    request.path.value().startsWith("/api/mobile") -> {
                        response.headers["X-Client-Type"] = "mobile"
                    }
                    request.path.value().startsWith("/api/web") -> {
                        response.headers["X-Client-Type"] = "web"
                    }
                    request.headers.getFirst("User-Agent")?.contains("Bot") == true -> {
                        response.headers["X-Robot-Detection"] = "true"
                    }
                }
            }
        )
    }

    override fun getOrder(): Int = -1
}

自定义响应头过滤器工厂

kotlin
@Component
class CustomSetResponseHeaderGatewayFilterFactory :
    AbstractGatewayFilterFactory<CustomSetResponseHeaderGatewayFilterFactory.Config>() {

    data class Config(
        var name: String = "",
        var value: String = "",
        var condition: String = ""
    )

    override fun apply(config: Config): GatewayFilter {
        return GatewayFilter { exchange, chain ->
            chain.filter(exchange).then(
                Mono.fromRunnable {
                    // 根据条件设置响应头
                    if (shouldSetHeader(exchange, config.condition)) {
                        exchange.response.headers[config.name] = config.value
                    }
                }
            )
        }
    }

    private fun shouldSetHeader(exchange: ServerWebExchange, condition: String): Boolean {
        // 实现条件判断逻辑
        return when (condition) {
            "success" -> exchange.response.statusCode?.is2xxSuccessful == true
            "error" -> exchange.response.statusCode?.isError == true
            else -> true
        }
    }

    override fun getConfigClass(): Class<Config> = Config::class.java
}

最佳实践

1. 响应头命名规范

建议使用 `X-` 前缀来标识自定义响应头,遵循 HTTP 标准规范。

kotlin
// 推荐的响应头命名
f.setResponseHeader("X-API-Version", "1.0")
f.setResponseHeader("X-Request-ID", requestId)
f.setResponseHeader("X-Service-Name", "user-service")

2. 性能考虑

避免在高并发场景下设置过多的响应头,这可能会影响性能。

kotlin
// 避免设置过多响应头
.filters { f ->
    f.setResponseHeader("X-Essential-Header", "value")
    // 避免设置非必要的响应头
}

3. 安全考虑

不要在响应头中暴露敏感信息,如内部服务地址、版本号等。

kotlin
// 错误示例 - 暴露敏感信息
f.setResponseHeader("X-Internal-Service", "user-service-192.168.1.100:8080") 

// 正确示例 - 只暴露必要信息
f.setResponseHeader("X-Service-Name", "user-service") 

监控和调试

启用响应头日志

kotlin
@Configuration
class GatewayLoggingConfig {

    @Bean
    fun responseHeaderLoggingFilter(): GlobalFilter {
        return GlobalFilter { exchange, chain ->
            chain.filter(exchange).doFinally {
                val headers = exchange.response.headers
                logger.info("Response headers: {}", headers.toSingleValueMap())
            }
        }
    }

    companion object {
        private val logger = LoggerFactory.getLogger(GatewayLoggingConfig::class.java)
    }
}

常见问题

Details

问题:响应头没有被设置 可能原因:

  1. 过滤器配置错误
  2. 路由匹配失败
  3. 过滤器执行顺序问题

解决方案:

  1. 检查过滤器配置语法
  2. 验证路由谓词配置
  3. 调整过滤器执行顺序
Details

问题:URI 变量无法解析 可能原因:

  1. 谓词配置中没有定义对应的变量
  2. 变量名拼写错误

解决方案:

  1. 确保在 predicates 中定义了相应的变量
  2. 检查变量名是否匹配

总结

SetResponseHeader 过滤器工厂是 Spring Cloud Gateway 中一个简单而强大的工具,它允许我们在网关层统一管理响应头。通过合理使用这个过滤器,我们可以:

  • 提升系统的安全性
  • 简化客户端的处理逻辑
  • 实现统一的响应头管理
  • 支持动态响应头设置

在实际使用中,需要注意性能影响和安全考虑,避免设置过多或暴露敏感信息的响应头。

NOTE

SetResponseHeader 会替换现有的同名响应头,如果需要添加响应头,请考虑使用 AddResponseHeader 过滤器工厂。