Appearance
AddRequestHeader
概述
AddRequestHeader 用于在请求转发到下游服务之前向请求头中添加自定义的头信息。这个过滤器在微服务架构中非常有用,可以帮助我们在网关层统一添加认证信息、追踪标识、业务标识等头信息。
业务场景
在实际的微服务架构中,AddRequestHeader 过滤器常用于以下场景:
- 统一认证信息传递:在网关层添加用户身份信息,避免每个微服务都要处理认证逻辑
- 链路追踪:添加请求追踪 ID,方便在分布式系统中追踪请求链路
- 业务标识传递:添加租户 ID、版本号等业务相关的头信息
- API 版本控制:为不同的路由添加版本标识
基本配置
AddRequestHeader 过滤器工厂接受两个参数:name(头名称)和 value(头值)。
基础使用示例
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("add_request_header_route") { predicateSpec ->
predicateSpec
.path("/api/users/**") // 匹配用户服务的路径
.filters { filterSpec ->
filterSpec
// 添加请求头:X-Service-Name: user-service
.addRequestHeader("X-Service-Name", "user-service")
// 添加认证相关的头信息
.addRequestHeader("X-Gateway-Auth", "gateway-token-123")
}
.uri("http://localhost:8081") // 用户服务地址
}
.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("add_request_header_route", predicateSpec ->
predicateSpec
.path("/api/users/**") // 匹配用户服务的路径
.filters(filterSpec ->
filterSpec
// 添加请求头:X-Service-Name: user-service
.addRequestHeader("X-Service-Name", "user-service")
// 添加认证相关的头信息
.addRequestHeader("X-Gateway-Auth", "gateway-token-123")
)
.uri("http://localhost:8081") // 用户服务地址
)
.build();
}
}YAML 配置方式
yaml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
# 语法:AddRequestHeader=header名称, header值
- AddRequestHeader=X-Request-red, blue这个配置会为所有匹配的请求添加 `X-Request-red: blue` 头信息到下游请求中。
高级功能:URI 变量支持
AddRequestHeader 过滤器支持使用 URI 变量,这些变量会在运行时被实际的路径参数替换。
动态头值示例
yaml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment} # 路径变量 segment
filters:
# 使用路径变量 {segment} 作为头值的一部分
- AddRequestHeader=X-Request-Red, Blue-{segment}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 DynamicHeaderConfig {
@Bean
fun dynamicHeaderRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// 用户服务路由 - 传递用户ID
.route("user_service_route") { predicateSpec ->
predicateSpec
.path("/api/users/{userId}/**")
.filters { filterSpec ->
filterSpec
// 将用户ID添加到请求头中,方便下游服务使用
.addRequestHeader("X-User-ID", "{userId}")
// 添加当前时间戳
.addRequestHeader("X-Request-Timestamp", "${System.currentTimeMillis()}")
}
.uri("http://user-service:8081")
}
// 订单服务路由 - 传递订单ID和用户ID
.route("order_service_route") { predicateSpec ->
predicateSpec
.path("/api/users/{userId}/orders/{orderId}")
.filters { filterSpec ->
filterSpec
.addRequestHeader("X-User-ID", "{userId}")
.addRequestHeader("X-Order-ID", "{orderId}")
.addRequestHeader("X-Business-Context", "user-{userId}-order-{orderId}")
}
.uri("http://order-service:8082")
}
.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 DynamicHeaderConfig {
@Bean
public RouteLocator dynamicHeaderRoutes(RouteLocatorBuilder builder) {
return builder.routes()
// 用户服务路由 - 传递用户ID
.route("user_service_route", predicateSpec ->
predicateSpec
.path("/api/users/{userId}/**")
.filters(filterSpec ->
filterSpec
// 将用户ID添加到请求头中,方便下游服务使用
.addRequestHeader("X-User-ID", "{userId}")
// 添加当前时间戳
.addRequestHeader("X-Request-Timestamp", String.valueOf(System.currentTimeMillis()))
)
.uri("http://user-service:8081")
)
// 订单服务路由 - 传递订单ID和用户ID
.route("order_service_route", predicateSpec ->
predicateSpec
.path("/api/users/{userId}/orders/{orderId}")
.filters(filterSpec ->
filterSpec
.addRequestHeader("X-User-ID", "{userId}")
.addRequestHeader("X-Order-ID", "{orderId}")
.addRequestHeader("X-Business-Context", "user-{userId}-order-{orderId}")
)
.uri("http://order-service:8082")
)
.build();
}
}实战案例
Details
微服务认证信息传递
在微服务架构中,我们经常需要在网关层进行统一认证,然后将认证信息传递给下游服务。
kotlin
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
import org.springframework.http.HttpHeaders
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
@Component
class AuthGatewayFilter : AbstractGatewayFilterFactory<AuthGatewayFilter.Config>() {
override fun apply(config: Config): GatewayFilter {
return GatewayFilter { exchange, chain ->
val request = exchange.request
val headers = request.headers
// 获取认证Token
val authToken = headers.getFirst(HttpHeaders.AUTHORIZATION)
if (!authToken.isNullOrEmpty()) {
// 解析Token获取用户信息(这里简化处理)
val userId = extractUserIdFromToken(authToken)
val userRole = extractUserRoleFromToken(authToken)
// 构建新的请求,添加用户信息到请求头
val mutatedRequest = request.mutate()
.header("X-User-ID", userId)
.header("X-User-Role", userRole)
.header("X-Auth-Source", "gateway")
.build()
val mutatedExchange = exchange.mutate()
.request(mutatedRequest)
.build()
chain.filter(mutatedExchange)
} else {
chain.filter(exchange)
}
}
}
/**
* 从Token中提取用户ID(实际项目中需要使用JWT解析)
*/
private fun extractUserIdFromToken(token: String): String {
// 这里只是示例,实际应该解析JWT Token
return "user-12345"
}
/**
* 从Token中提取用户角色
*/
private fun extractUserRoleFromToken(token: String): String {
// 这里只是示例,实际应该解析JWT Token
return "ROLE_USER"
}
override fun getConfigClass(): Class<Config> = Config::class.java
class Config {
// 配置类,可以添加一些配置参数
}
}java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
@Component
public class AuthGatewayFilter extends AbstractGatewayFilterFactory<AuthGatewayFilter.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
var request = exchange.getRequest();
var headers = request.getHeaders();
// 获取认证Token
String authToken = headers.getFirst(HttpHeaders.AUTHORIZATION);
if (authToken != null && !authToken.isEmpty()) {
// 解析Token获取用户信息(这里简化处理)
String userId = extractUserIdFromToken(authToken);
String userRole = extractUserRoleFromToken(authToken);
// 构建新的请求,添加用户信息到请求头
var mutatedRequest = request.mutate()
.header("X-User-ID", userId)
.header("X-User-Role", userRole)
.header("X-Auth-Source", "gateway")
.build();
var mutatedExchange = exchange.mutate()
.request(mutatedRequest)
.build();
return chain.filter(mutatedExchange);
} else {
return chain.filter(exchange);
}
};
}
/**
* 从Token中提取用户ID(实际项目中需要使用JWT解析)
*/
private String extractUserIdFromToken(String token) {
// 这里只是示例,实际应该解析JWT Token
return "user-12345";
}
/**
* 从Token中提取用户角色
*/
private String extractUserRoleFromToken(String token) {
// 这里只是示例,实际应该解析JWT Token
return "ROLE_USER";
}
@Override
public Class<Config> getConfigClass() {
return Config.class;
}
public static class Config {
// 配置类,可以添加一些配置参数
}
}Details
链路追踪 ID 传递
yaml
spring:
cloud:
gateway:
routes:
- id: tracing_route
uri: http://downstream-service
predicates:
- Path=/api/**
filters:
# 添加链路追踪ID
- AddRequestHeader=X-Trace-ID, ${random.uuid}
# 添加网关标识
- AddRequestHeader=X-Gateway-Instance, gateway-001
# 添加请求时间戳
- AddRequestHeader=X-Request-Time, ${T(java.time.Instant).now()}Details
与其他过滤器组合使用,构建更复杂的网关功能
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 CombinedFiltersConfig {
@Bean
fun combinedFiltersRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("api_route") { predicateSpec ->
predicateSpec
.path("/api/**")
.filters { filterSpec ->
filterSpec
// 1. 限流过滤器
.requestRateLimiter { rateLimiterSpec ->
rateLimiterSpec
.rateLimiter { redisRateLimiter ->
redisRateLimiter
.replenishRate(10) // 每秒允许10个请求
.burstCapacity(20) // 突发容量20
.requestedTokens(1) // 每个请求消耗1个令牌
}
.keyResolver { exchange ->
// 基于客户端IP进行限流
reactor.core.publisher.Mono.just(
exchange.request.remoteAddress?.address?.hostAddress ?: "unknown"
)
}
}
// 2. 添加请求头信息
.addRequestHeader("X-Rate-Limited", "true")
.addRequestHeader("X-Client-IP", "#{T(java.net.InetAddress).getLocalHost().getHostAddress()}")
// 3. 重写路径
.rewritePath("/api/(?<segment>.*)", "/v1/\${segment}")
// 4. 添加响应头
.addResponseHeader("X-Gateway-Response", "processed")
}
.uri("http://backend-service:8080")
}
.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 CombinedFiltersConfig {
@Bean
public RouteLocator combinedFiltersRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("api_route", predicateSpec ->
predicateSpec
.path("/api/**")
.filters(filterSpec ->
filterSpec
// 1. 限流过滤器
.requestRateLimiter(rateLimiterSpec ->
rateLimiterSpec
.rateLimiter(redisRateLimiter ->
redisRateLimiter
.replenishRate(10) // 每秒允许10个请求
.burstCapacity(20) // 突发容量20
.requestedTokens(1) // 每个请求消耗1个令牌
)
.keyResolver(exchange ->
// 基于客户端IP进行限流
reactor.core.publisher.Mono.just(
exchange.getRequest().getRemoteAddress() != null ?
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() :
"unknown"
)
)
)
// 2. 添加请求头信息
.addRequestHeader("X-Rate-Limited", "true")
.addRequestHeader("X-Client-IP", "#{T(java.net.InetAddress).getLocalHost().getHostAddress()}")
// 3. 重写路径
.rewritePath("/api/(?<segment>.*)", "/v1/${segment}")
// 4. 添加响应头
.addResponseHeader("X-Gateway-Response", "processed")
)
.uri("http://backend-service:8080")
)
.build();
}
}最佳实践
1. 头信息命名规范
建议使用统一的前缀来标识网关添加的头信息,如 `X-Gateway-`、`X-Custom-` 等,这样便于区分和管理。
kotlin
// 推荐的头信息命名
.addRequestHeader("X-Gateway-User-ID", "{userId}")
.addRequestHeader("X-Gateway-Timestamp", "${System.currentTimeMillis()}")
.addRequestHeader("X-Gateway-Version", "v1.0")
// 不推荐的命名
.addRequestHeader("user", "{userId}") // 太简单,可能冲突
.addRequestHeader("id", "{userId}") // 语义不明确2. 避免敏感信息泄露
不要在请求头中传递密码、私钥等敏感信息。如果需要传递认证信息,建议使用加密的 Token。
kotlin
// ❌ 错误做法 - 直接传递敏感信息
.addRequestHeader("X-User-Password", "plain-password")
// ✅ 正确做法 - 传递加密的Token或ID
.addRequestHeader("X-User-Token", "encrypted-jwt-token")
.addRequestHeader("X-User-ID", "{userId}")3. 性能考虑
避免在请求头中添加过大的数据,这会影响网络传输性能。如果需要传递大量数据,考虑使用缓存或数据库。
常见问题与解决方案
问题 1:头信息未生效
问题现象:配置了 AddRequestHeader 但下游服务接收不到头信息。
可能原因:
- 过滤器配置语法错误
- 路由匹配条件不正确
- 下游服务获取头信息的方式不正确
解决方案:
kotlin
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
import org.springframework.stereotype.Component
import org.slf4j.LoggerFactory
@Component
class DebugHeaderFilter : AbstractGatewayFilterFactory<DebugHeaderFilter.Config>() {
private val logger = LoggerFactory.getLogger(DebugHeaderFilter::class.java)
override fun apply(config: Config): GatewayFilter {
return GatewayFilter { exchange, chain ->
val request = exchange.request
// 打印所有请求头信息,用于调试
logger.info("=== 请求头信息 ===")
request.headers.forEach { (name, values) ->
logger.info("Header: $name = $values")
}
logger.info("=== 请求头信息结束 ===")
chain.filter(exchange)
}
}
override fun getConfigClass(): Class<Config> = Config::class.java
class Config
}java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
public class DebugHeaderFilter extends AbstractGatewayFilterFactory<DebugHeaderFilter.Config> {
private static final Logger logger = LoggerFactory.getLogger(DebugHeaderFilter.class);
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
var request = exchange.getRequest();
// 打印所有请求头信息,用于调试
logger.info("=== 请求头信息 ===");
request.getHeaders().forEach((name, values) -> {
logger.info("Header: {} = {}", name, values);
});
logger.info("=== 请求头信息结束 ===");
return chain.filter(exchange);
};
}
@Override
public Class<Config> getConfigClass() {
return Config.class;
}
public static class Config {
}
}问题 2:URI 变量未正确替换
问题现象:使用了 {变量名} 语法,但实际请求头中显示的还是字面量 {变量名}。
解决方案:确保路径谓词正确匹配,并且变量名称一致。
yaml
# ❌ 错误配置 - 变量名不匹配
spring:
cloud:
gateway:
routes:
- id: wrong_variable_route
predicates:
- Path=/users/{userId}/**
filters:
- AddRequestHeader=X-User-ID, {userID} # 注意这里是 userID,与路径中的 userId 不匹配
# ✅ 正确配置 - 变量名匹配
spring:
cloud:
gateway:
routes:
- id: correct_variable_route
predicates:
- Path=/users/{userId}/**
filters:
- AddRequestHeader=X-User-ID, {userId} # 变量名与路径中的一致总结
AddRequestHeader 过滤器工厂是 Spring Cloud Gateway 中一个简单但非常实用的功能组件。通过合理使用这个过滤器,我们可以:
- 统一处理认证信息:在网关层添加用户身份、权限等信息
- 实现链路追踪:添加追踪 ID,便于分布式系统的问题排查
- 传递业务上下文:将路径参数、业务标识等信息传递给下游服务
- 实现 API 版本控制:为不同版本的 API 添加版本标识
在实际使用中,建议结合日志、监控等手段,确保头信息正确传递,并注意性能和安全方面的考虑。通过合理的配置和最佳实践,`AddRequestHeader` 可以大大简化微服务间的信息传递逻辑。