bigywater.tech
Spring · 121

Spring Cloud Gateway:Route、Predicate 与 Filter

网关路由是怎么匹配的?多个谓词是与还是或?后端能看到客户端真实 IP 吗?后端挂了网关返回 502 还是 500?为什么按老文章写的 spring.cloud.gateway.routes 一条路由都不生效?这篇在 Spring Boot 4.0.8、Spring Cloud Gateway 5.0.3 上用一个会记录请求的后端逐项实测。

不加 X-Forwarded
默认后端收不到 Forwarded / X-Forwarded-*,客户端自带的也被丢掉,要配 trusted-proxies
500
后端连接不上时网关返回的状态码,不是 502 / 503
不生效
路由写在旧的 spring.cloud.gateway.routes 前缀下,没有报错,但 GET 得到 404

谓词与过滤器

实测 · 本机执行输出
  GET /api/orders/42(StripPrefix=1) → 网关返回 200,后端收到 GET /orders/42
  GET /h/a/b(AddRequestHeader + RewritePath + AddResponseHeader) → 网关返回 200,后端收到 GET /echo/a/b;x-from=gw
    网关返回的响应头里 x-resp = yes,x-backend = yes
  GET /pre/x(StripPrefix=1 再 PrefixPath=/v2) → 网关返回 200,后端收到 GET /v2/x
  POST /pre/x(Method=GET 的路由不匹配 POST) → 网关返回 404 {"timestamp":"2026-10-22T01:08:35.823Z","path":"/pre/x","status":404,"error":"Not Found",",后端没有收到请求
  GET /c/q 带 X-Canary: 7 与 ?debug=1(Header 正则 + Query 同时满足) → 网关返回 200,后端收到 GET /canary-hit?debug=1
  GET /c/q 带 X-Canary: abc(Header 正则不满足) → 网关返回 404 {"timestamp":"2026-10-22T01:08:36.157Z","path":"/c/q","status":404,"error":"Not Found","re,后端没有收到请求
  GET /c/q 带 X-Canary: 7 但没有 ?debug(Query 不满足) → 网关返回 404 {"timestamp":"2026-10-22T01:08:36.311Z","path":"/c/q","status":404,"error":"Not Found","re,后端没有收到请求
  GET /nothing(没有任何路由匹配) → 网关返回 404 {"timestamp":"2026-10-22T01:08:36.470Z","path":"/nothing","status":404,"error":"Not Found",后端没有收到请求

谓词是与关系;StripPrefix、PrefixPath、SetPath、RewritePath 改路径;AddRequestHeader / AddResponseHeader 改头。

请求头

实测 · 本机执行输出
  客户端发 Host: gw.example.com、X-Custom: abc、Accept: application/json;后端收到的请求头:accept: application/json; accept-encoding: identity; host: 127.0.0.1:50633; user-agent: ReactorNetty/1.3.7; x-custom: abc
  客户端自己带 X-Forwarded-For: 1.2.3.4 和 Forwarded: for=9.9.9.9,没配 trusted-proxies:后端收到的 forwarded 相关头 = {}
  配 trusted-proxies=.* 之后(客户端 Host: gw.example.com):后端收到 forwarded: proto=http;host=gw.example.com;for="127.0.0.1:50848"; x-forwarded-for: 127.0.0.1; x-forwarded-host: gw.example.com; x-forwarded-port: 80; x-forwarded-prefix: /api; x-forwarded-proto: http
  同样配置,客户端自己带 X-Forwarded-For: 1.2.3.4:后端收到 x-forwarded-for = 1.2.3.4,127.0.0.1
  加上 PreserveHostHeader 过滤器,后端收到的 host = gw.example.com

默认不转发 Forwarded;trusted-proxies 打开后才加;Host 默认是后端自己的。

匹配顺序

实测 · 本机执行输出
  两条路由都匹配 /api/x,按声明顺序 first、second → 命中的是 first
  给 second 设 order=-1 → 命中的是 second

order 小的在前,同 order 按声明顺序。

出错

实测 · 本机执行输出
  后端端口不可连接(127.0.0.1:1) → 网关返回 500,耗时 1.2 秒,响应体 {"timestamp":"2026-10-22T01:09:10.123Z","path":"/dead/x","status":500,"error":"Internal Server Error","requestId":"8034367e-1"}
  后端 3 秒才响应,response-timeout=1s → 网关返回 504,耗时 1.0 秒,响应体 {"timestamp":"2026-10-22T01:09:11.228Z","path":"/slow/x","status":504,"error":"Gateway Timeout","requestId":"b25cfe6d-2"}

连不上 500,响应超时 504。

配置前缀与 actuator

实测 · 本机执行输出
  路由写在旧前缀 spring.cloud.gateway.routes 下,GET /api/orders/1 → 网关返回 404 {"timestamp":"2026-10-22T01:09:15.603Z","path":"/api/orders/1","status":404,"error":"Not F,后端没有收到请求
  只配了 management.endpoints.web.exposure.include=gateway:GET /actuator → 200 {"_links":{"self":{"href":"http://127.0.0.1:9613/actuator","templated":false}}};GET /actuator/gateway/routes → 404
  再加 management.endpoint.gateway.access=unrestricted:GET /actuator/gateway/routes → 200 [{"predicate":"(RouteDefinitionRouteLocator$$Lambda/0x0000008001616b48 && Paths: [/api/**], match trailing slash: true)","route_id":"orders","filters":["[[StripPrefix parts = 1], order = 1]"],"uri":"http://127.0.0.1:50633","order":0}]

新前缀 spring.cloud.gateway.server.webflux;gateway 端点要 access=unrestricted。

分步演示

Gateway 路由
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。后端是 Python 线程里的 HTTP 服务,记录收到的方法、路径与请求头;客户端是 Python http.client,直接连网关;路由全部用 -D 属性配置;耗时是整体耗时,有零点几秒的抖动
    • 没有验证:Gateway Server MVC 版(spring-cloud-starter-gateway-server-webmvc)的行为;Java DSL(RouteLocatorBuilder)定义的路由;服务发现自动路由(discovery locator);自定义 Predicate / Filter 工厂;GlobalFilter 的顺序(见第 122 篇);限流与熔断(第 124、127 篇);WebSocket 与 SSE;请求体较大时的缓冲;trusted-proxies 的正则匹配细节与 X-Forwarded-Port 的取值规则;连接不上后端为什么是 500(只观察到状态码,没有看日志)
    京ICP备2026067502号-1京公网安备11010502063487号