Appearance
SetStatus GatewayFilter 工厂
概述
SetStatus GatewayFilter 工厂是 Spring Cloud Gateway 提供的一个重要过滤器,用于修改 HTTP 响应的状态码。这个过滤器在微服务架构中非常有用,特别是在需要统一处理响应状态或者进行状态码转换的场景中。
解决的问题
在微服务架构中,我们经常遇到以下场景:
- 服务降级:当下游服务不可用时,需要返回特定的状态码
- 统一错误处理:将不同的错误统一转换为标准的 HTTP 状态码
- API 版本兼容:在 API 升级过程中,需要返回特定的状态码来保持兼容性
- 安全控制:隐藏真实的服务状态,返回统一的错误码
配置参数
SetStatus 过滤器接受一个参数 status,它必须是一个有效的 Spring HttpStatus:
- 整数值:如
404、401、500等 - 枚举字符串:如
NOT_FOUND、UNAUTHORIZED、INTERNAL_SERVER_ERROR等
基本使用
YAML 配置方式
yaml
spring:
cloud:
gateway:
routes:
# 使用枚举字符串配置
- id: setstatusstring_route
uri: https://example.org
filters:
- SetStatus=UNAUTHORIZED
# 使用整数值配置
- id: setstatusint_route
uri: https://example.org
filters:
- SetStatus=401Kotlin 编程式配置
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
import org.springframework.http.HttpStatus
@Configuration
class GatewayConfig {
@Bean
fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// 使用枚举配置状态码
.route("setstatusstring_route") { r ->
r.path("/api/unauthorized/**")
.filters { f ->
f.setStatus(HttpStatus.UNAUTHORIZED)
}
.uri("https://example.org")
}
// 使用整数配置状态码
.route("setstatusint_route") { r ->
r.path("/api/forbidden/**")
.filters { f ->
f.setStatus(403)
}
.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;
import org.springframework.http.HttpStatus;
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("setstatusstring_route", r ->
r.path("/api/unauthorized/**")
.filters(f -> f.setStatus(HttpStatus.UNAUTHORIZED))
.uri("https://example.org")
)
.route("setstatusint_route", r ->
r.path("/api/forbidden/**")
.filters(f -> f.setStatus(403))
.uri("https://example.org")
)
.build();
}
}保留原始状态码
有时候我们需要在设置新状态码的同时,保留原始的 HTTP 状态码信息。SetStatus 过滤器支持将原始状态码添加到响应头中:
配置原始状态码头部
yaml
spring:
cloud:
gateway:
set-status:
original-status-header-name: original-http-status
routes:
- id: preserve_original_status
uri: https://example.org
filters:
- SetStatus=503TIP
配置 original-status-header-name 后,原始状态码将会添加到响应头中,便于调试和监控。
实际业务场景
场景 1:服务降级处理
当下游服务不可用时,我们希望返回服务不可用的状态码:
kotlin
@Configuration
class ServiceDegradationConfig {
@Bean
fun serviceDownRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("service_degradation") { r ->
r.path("/api/payment/**")
.filters { f ->
f.setStatus(HttpStatus.SERVICE_UNAVAILABLE) // 503 状态码
}
.uri("https://payment-service.com")
}
.build()
}
}场景 2:API 权限控制
对于需要权限验证的 API,返回统一的未授权状态:
kotlin
@Configuration
class AuthControlConfig {
@Bean
fun authRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("admin_api") { r ->
r.path("/admin/**")
.and()
.not { it.header("Authorization") } // 没有 Authorization 头部
.filters { f ->
f.setStatus(HttpStatus.UNAUTHORIZED) // 返回 401
}
.uri("https://admin-service.com")
}
.build()
}
}场景 3:维护模式
当系统处于维护状态时,返回维护状态码:
kotlin
@Configuration
class MaintenanceConfig {
@Bean
fun maintenanceRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("maintenance_mode") { r ->
r.path("/**")
.and()
.header("X-Maintenance-Mode", "true")
.filters { f ->
f.setStatus(HttpStatus.SERVICE_UNAVAILABLE) // 503 状态码
f.setResponseHeader("Retry-After", "3600") // 1小时后重试
}
.uri("https://maintenance-page.com")
}
.build()
}
}工作流程
常用状态码示例
完整配置示例
kotlin
@Configuration
class StatusCodeConfig {
@Bean
fun statusRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// 客户端错误 4xx
.route("bad_request") { r ->
r.path("/api/invalid/**")
.filters { f -> f.setStatus(HttpStatus.BAD_REQUEST) } // 400
.uri("https://service.com")
}
.route("unauthorized") { r ->
r.path("/api/auth/**")
.filters { f -> f.setStatus(HttpStatus.UNAUTHORIZED) } // 401
.uri("https://service.com")
}
.route("forbidden") { r ->
r.path("/api/admin/**")
.filters { f -> f.setStatus(HttpStatus.FORBIDDEN) } // 403
.uri("https://service.com")
}
.route("not_found") { r ->
r.path("/api/deprecated/**")
.filters { f -> f.setStatus(HttpStatus.NOT_FOUND) } // 404
.uri("https://service.com")
}
// 服务器错误 5xx
.route("server_error") { r ->
r.path("/api/error/**")
.filters { f -> f.setStatus(HttpStatus.INTERNAL_SERVER_ERROR) } // 500
.uri("https://service.com")
}
.route("service_unavailable") { r ->
r.path("/api/maintenance/**")
.filters { f -> f.setStatus(HttpStatus.SERVICE_UNAVAILABLE) } // 503
.uri("https://service.com")
}
.build()
}
}高级用法
条件性状态设置
结合其他过滤器实现条件性状态设置:
kotlin
@Configuration
class ConditionalStatusConfig {
@Bean
fun conditionalRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("conditional_status") { r ->
r.path("/api/data/**")
.filters { f ->
f.circuitBreaker { config ->
config.name = "data-service-cb"
config.fallbackUri = "forward:/fallback"
}
.setStatus(HttpStatus.SERVICE_UNAVAILABLE) // 熔断时返回 503
}
.uri("https://data-service.com")
}
.build()
}
}自定义状态码处理器
kotlin
@Component
class CustomStatusHandler {
fun handleCustomStatus(exchange: ServerWebExchange): Mono<Void> {
val response = exchange.response
val request = exchange.request
// 根据请求路径设置不同状态码
when {
request.path.value().contains("/v1/") -> {
response.statusCode = HttpStatus.GONE // 410 - API 版本已弃用
}
request.path.value().contains("/beta/") -> {
response.statusCode = HttpStatus.ACCEPTED // 202 - Beta 版本
}
else -> {
response.statusCode = HttpStatus.OK
}
}
return response.setComplete()
}
}注意事项
WARNING
设置状态码后,原始响应的状态码会被覆盖,确保这是你想要的行为。
IMPORTANT
如果需要保留原始状态码信息,务必配置 original-status-header-name 属性。
TIP
在生产环境中,建议记录状态码变更的日志,便于排查问题。
监控和调试
添加日志记录
kotlin
@Configuration
class StatusLoggingConfig {
@Bean
fun loggingRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("logged_status") { r ->
r.path("/api/monitored/**")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = RedisRateLimiter(10, 20)
config.keyResolver = IpKeyResolver()
}
.setStatus(HttpStatus.TOO_MANY_REQUESTS) // 429 - 限流
}
.uri("https://monitored-service.com")
}
.build()
}
}健康检查集成
kotlin
@Component
class GatewayHealthIndicator : HealthIndicator {
override fun health(): Health {
return try {
// 检查网关状态
Health.up()
.withDetail("status", "Gateway is running")
.withDetail("setStatusFilter", "active")
.build()
} catch (e: Exception) {
Health.down()
.withDetail("error", e.message)
.build()
}
}
}总结
SetStatus GatewayFilter 工厂是一个简单但强大的工具,它可以帮助我们:
- 🎯 统一状态码管理:在网关层统一处理和转换 HTTP 状态码
- 🛡️ 安全控制:隐藏真实的服务状态,提供统一的错误响应
- 🔄 服务降级:在服务不可用时返回合适的状态码
- 📊 监控友好:通过保留原始状态码便于监控和调试
- 🚀 灵活配置:支持 YAML 和编程式两种配置方式
通过合理使用 SetStatus 过滤器,可以显著提升微服务架构中的错误处理和用户体验。