Skip to content

StripPrefix 去掉前缀

概述

StripPrefix 的主要作用是在将请求转发到下游服务之前,从请求路径中剥离指定数量的路径段。这在微服务架构中非常有用,特别是当网关的路由路径与后端服务的实际路径不完全匹配时。

TIP

StripPrefix 过滤器通常用于解决路由前缀与后端服务路径不匹配的问题,让你可以在网关层面定义统一的路由规则,而不需要修改后端服务的接口路径。

工作原理

StripPrefix 过滤器接受一个参数 parts,该参数指定了要从请求路径中移除的路径段数量。当请求通过网关时,过滤器会按照指定的数量删除路径前缀,然后将修改后的请求转发给下游服务。

配置示例

基础配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: nameRoot # 路由ID,用于标识该路由规则
          uri: https://nameservice # 目标服务地址
          predicates:
            - Path=/name/** # 路径匹配规则,匹配 /name/ 开头的所有请求
          filters:
            - StripPrefix=2 # 剥离前2个路径段
java
@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("nameRoot", r -> r
                .path("/name/**")                    // 路径匹配
                .filters(f -> f.stripPrefix(2))      // 剥离前2个路径段
                .uri("https://nameservice"))         // 目标服务
            .build();
    }
}

Kotlin DSL 配置

kotlin
@Configuration
class GatewayConfig {

    @Bean
    fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes {
            route("nameRoot") {
                path("/name/**")                    // 匹配以 /name/ 开头的所有路径
                filters {
                    stripPrefix(2)                  // 移除前2个路径段
                }
                uri("https://nameservice")          // 转发到目标服务
            }
        }.build()
    }
}

实际业务场景

场景 1:API 版本管理

在实际开发中,我们经常需要在网关层面统一管理 API 版本,但后端服务可能不包含版本信息:

yaml
spring:
  cloud:
    gateway:
      routes:
        # 处理 v1 版本的用户服务请求
        - id: user-service-v1
          uri: http://user-service:8080
          predicates:
            - Path=/api/v1/users/**
          filters:
            - StripPrefix=3 # 移除 /api/v1/users,只保留具体的用户操作路径

请求转换示例:

  • 客户端请求:GET /api/v1/users/profile/123
  • 转发到服务:GET /profile/123

场景 2:多租户路由

在多租户应用中,我们可能需要根据租户 ID 路由到不同的服务实例:

kotlin
@Configuration
class MultiTenantGatewayConfig {

    @Bean
    fun tenantRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes {
            // 企业租户路由
            route("enterprise-tenant") {
                path("/tenant/enterprise/**")
                filters {
                    stripPrefix(2)              // 移除 /tenant/enterprise
                    addRequestHeader("X-Tenant-Type", "enterprise")
                }
                uri("http://enterprise-service:8080")
            }

            // 个人租户路由
            route("personal-tenant") {
                path("/tenant/personal/**")
                filters {
                    stripPrefix(2)              // 移除 /tenant/personal
                    addRequestHeader("X-Tenant-Type", "personal")
                }
                uri("http://personal-service:8080")
            }
        }.build()
    }
}

高级用法

与其他过滤器组合使用

StripPrefix 经常与其他过滤器组合使用,以实现更复杂的路由逻辑:

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: secure-api
          uri: http://backend-service:8080
          predicates:
            - Path=/secure/api/**
          filters:
            - StripPrefix=2 # 先移除路径前缀
            - AddRequestHeader=X-Gateway-Source, spring-cloud-gateway
            - RewritePath=/old/(.*), /new/$1 # 重写路径
            - RequestRateLimiter=10,1s # 限流

动态路径剥离

kotlin
@Component
class DynamicStripPrefixFilter : GatewayFilter {

    override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
        val request = exchange.request
        val path = request.uri.path

        // 根据业务逻辑动态决定剥离的段数
        val stripCount = when {
            path.startsWith("/api/v1/") -> 3
            path.startsWith("/api/") -> 2
            else -> 1
        }

        // 构建新的请求路径
        val newPath = stripPrefixFromPath(path, stripCount)
        val newRequest = request.mutate()
            .path(newPath)
            .build()

        return chain.filter(exchange.mutate().request(newRequest).build())
    }

    private fun stripPrefixFromPath(path: String, parts: Int): String {
        val pathSegments = path.split("/").filter { it.isNotEmpty() }
        return if (pathSegments.size > parts) {
            "/" + pathSegments.drop(parts).joinToString("/")
        } else {
            "/"
        }
    }
}

注意事项与最佳实践

WARNING

使用 StripPrefix 时需要确保剥离后的路径仍然是有效的,避免剥离过多导致路径为空或无效。

IMPORTANT

在设计路由规则时,要考虑到前端和后端的路径约定,确保 StripPrefix 的使用不会破坏 RESTful API 的语义。

最佳实践

  1. 路径设计一致性

    yaml
    # 推荐:保持路径语义的一致性
    - Path=/service/user/**
    - StripPrefix=2 # 剥离 /service/user,保留具体操作路径
  2. 错误处理

    kotlin
    @Component
    class SafeStripPrefixFilter : GatewayFilter {
    
        override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
            return try {
                val request = exchange.request
                val originalPath = request.uri.path
    
                // 验证路径段数量是否足够
                val segments = originalPath.split("/").filter { it.isNotEmpty() }
                if (segments.size < stripCount) {
                    return handleInvalidPath(exchange)
                }
    
                // 执行路径剥离逻辑
                // ...
    
                chain.filter(exchange)
            } catch (e: Exception) {
                handleError(exchange, e)
            }
        }
    }
  3. 监控和日志

    kotlin
    @Slf4j
    @Component
    class LoggingStripPrefixFilter : GatewayFilter {
    
        override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
            val request = exchange.request
            val originalPath = request.uri.path
    
            log.info("原始请求路径: {}", originalPath)
    
            return chain.filter(exchange).doOnSuccess {
                val finalPath = exchange.request.uri.path
                log.info("转发路径: {} -> {}", originalPath, finalPath)
            }
        }
    }

常见问题

Q: StripPrefix 和 RewritePath 有什么区别?

NOTE

  • StripPrefix 是简单地移除指定数量的路径段
  • RewritePath 可以使用正则表达式进行更复杂的路径重写
  • StripPrefix 性能更好,适用于简单的前缀移除场景

Q: 如何处理根路径问题?

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: root-path-handling
          uri: http://backend:8080
          predicates:
            - Path=/api/**
          filters:
            - StripPrefix=1
            - RewritePath=^$, / # 确保空路径重写为根路径

总结

StripPrefix 过滤器是 Spring Cloud Gateway 中一个简单但强大的工具,它帮助我们在网关层面灵活地处理路径映射问题。通过合理使用这个过滤器,我们可以:

  • 实现统一的 API 版本管理
  • 简化多租户架构的路由配置
  • 保持前后端路径设计的灵活性
  • 提高微服务架构的可维护性

TIP

在实际项目中,建议将 StripPrefix 与其他过滤器组合使用,并添加适当的监控和错误处理机制,以确保网关的稳定性和可观测性。