Gateway - 内置 Filter 使用指南:AddRequestHeader、RewritePath 等实战

Gateway - 内置 Filter 使用指南:AddRequestHeader、RewritePath 等实战
在这里插入图片描述
👋 大家好,欢迎来到我的技术博客!
💻 作为一名热爱 Java 与软件开发的程序员,我始终相信:清晰的逻辑 + 持续的积累 = 稳健的成长
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Gateway这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!

文章目录

Gateway - 内置 Filter 使用指南:AddRequestHeader、RewritePath 等实战 🚀

在现代微服务架构中,API 网关扮演着至关重要的角色。它作为系统的统一入口,负责请求路由、负载均衡、安全控制、限流熔断、协议转换等多种功能。Spring Cloud Gateway 作为 Spring 官方推荐的 API 网关实现,提供了强大的路由和过滤机制。其中,内置 Filter 是其核心特性之一,允许我们在请求到达目标服务之前或之后执行特定的操作。

本文将深入探讨 Spring Cloud Gateway 中常用的 内置 Filter,特别是 AddRequestHeaderRewritePath,并结合实际 Java 代码示例进行演示。我们将从基础概念出发,逐步引导你掌握这些 Filter 的使用方法,并通过图表和代码片段来加深理解。

一、Spring Cloud Gateway 概述 📚

1.1 什么是 Spring Cloud Gateway?

Spring Cloud Gateway 是 Spring 官方推出的下一代 API 网关,基于 Spring Framework 5、Project Reactor 和 Spring Boot 2 构建。它旨在提供一种简单而有效的方式来路由到任何后端服务,并且能够轻松地添加横切关注点(如安全性、监控、响应压缩等)。

与传统的 Zuul 相比,Gateway 提供了更强大的路由匹配能力、更好的性能以及更丰富的过滤器机制。它基于 Netty 异步非阻塞 I/O 模型,能够处理高并发请求。

1.2 核心组件

Spring Cloud Gateway 的核心组件包括:

  • Route (路由): 路由是网关的基本单元。它由一个 ID、一个目标 URI、一组断言(Predicate)和一组过滤器(Filter)组成。当请求满足某个断言条件时,请求会被转发到指定的 URI。
  • Predicate (断言): 断言用于判断请求是否符合路由规则。例如,可以根据请求的路径、方法、头信息等进行匹配。
  • Filter (过滤器): 过滤器可以在请求被路由之前或之后执行操作。它们可以用来修改请求或响应,或者执行诸如身份验证、限流等操作。

1.3 Filter 类型

Spring Cloud Gateway 提供了多种类型的 Filter:

  • 内置 Filter: 由 Spring Cloud Gateway 提供的预定义过滤器,无需额外配置即可使用。例如 AddRequestHeader, RewritePath, SetStatus 等。
  • 自定义 Filter: 开发者可以根据业务需求编写自己的 Filter,实现特定的功能逻辑。

二、内置 Filter 详解 🔍

2.1 AddRequestHeader Filter 💬

2.1.1 功能简介

AddRequestHeader 是一个非常实用的内置 Filter,它的主要作用是在请求发送到下游服务之前,向请求头(Headers)中添加一个或多个自定义的键值对。这对于传递服务间调用所需的信息(如认证令牌、追踪 ID 等)非常有用。

2.1.2 配置方式

可以通过 YAML 或 Java 配置方式来使用 AddRequestHeader

YAML 配置示例
spring:cloud:gateway:routes:-id: add-header-route uri: lb://service-a # 假设 service-a 是你的服务名predicates:- Path=/api/service-a/**filters:-name: AddRequestHeader args:name: X-Forwarded-For value: 192.168.1.1 -name: AddRequestHeader args:name: X-Custom-Header value: MyValue 

这个配置表示,当请求路径以 /api/service-a/ 开头时,网关会自动在请求头中添加两个字段:

  • X-Forwarded-For: 192.168.1.1
  • X-Custom-Header: MyValue
Java 配置示例
importorg.springframework.cloud.gateway.route.RouteLocator;importorg.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassGatewayConfig{@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilder builder){return builder.routes().route("add-header-route", r -> r.path("/api/service-a/**").filters(f -> f.addRequestHeader("X-Forwarded-For","192.168.1.1").addRequestHeader("X-Custom-Header","MyValue")).uri("lb://service-a"))// 假设 service-a 是你的服务名.build();}}
2.1.3 实际应用案例

假设我们有一个用户服务 user-service,它需要知道请求来自哪个客户端,以便进行日志记录和安全审计。我们可以利用 AddRequestHeader 来实现。

spring:cloud:gateway:routes:-id: user-service-route uri: lb://user-service predicates:- Path=/api/users/**filters:-name: AddRequestHeader args:name: X-Client-ID value: ${client.id:default-client}# 可以从环境变量或配置文件获取-name: AddRequestHeader args:name: X-Request-Timestamp value: ${timestamp:${now}}# 这里使用 SpEL 表达式

或者在 Java 配置中:

@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilder builder){return builder.routes().route("user-service-route", r -> r.path("/api/users/**").filters(f -> f.addRequestHeader("X-Client-ID","${client.id:default-client}").addRequestHeader("X-Request-Timestamp",String.valueOf(System.currentTimeMillis()))).uri("lb://user-service")).build();}
2.1.4 注意事项
  • namevalue 参数都是必需的。
  • value 支持占位符和 SpEL 表达式,这使得动态设置值成为可能。
  • 添加的 Header 会在请求转发到下游服务时一同发送。

2.2 RewritePath Filter 🔄

2.2.1 功能简介

RewritePath Filter 允许你在请求到达下游服务之前,修改请求的路径(Path)。这是非常有用的,尤其是在需要将网关暴露的 API 路径与后端服务的内部路径不一致时。例如,网关可能希望将 /api/v1/users 映射到后端服务的 /users 路径。

2.2.2 配置方式
YAML 配置示例
spring:cloud:gateway:routes:-id: rewrite-path-route uri: lb://backend-service predicates:- Path=/api/v1/**filters:-name: RewritePath args:regexp: /api/v1/(?<segment>.*)# 定义一个正则表达式,捕获后面的路径部分replacement: /$\{segment}# 使用捕获组替换原路径

这个配置的意思是:当请求路径是 /api/v1/... 时,将路径重写为 /...。例如,请求 /api/v1/users/123 会被重写为 /users/123 并转发到 backend-service

Java 配置示例
importorg.springframework.cloud.gateway.route.RouteLocator;importorg.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassGatewayConfig{@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilder builder){return builder.routes().route("rewrite-path-route", r -> r.path("/api/v1/**").filters(f -> f.rewritePath("/api/v1/(?<segment>.*)","/${segment}")).uri("lb://backend-service")).build();}}
2.2.3 实际应用案例

考虑一个场景,我们的前端应用通过网关访问后端服务。前端可能期望 API 路径是 /api/v2/products,但后端服务的接口设计是 /products。这时,我们就可以使用 RewritePath 来实现映射。

spring:cloud:gateway:routes:-id: product-service-route uri: lb://product-service predicates:- Path=/api/v2/products/**filters:-name: RewritePath args:regexp: /api/v2/products/(?<rest>.*)replacement: /products/${rest}

这个配置会将请求 /api/v2/products/123 重写为 /products/123

2.2.4 正则表达式详解

RewritePath 的关键在于 regexpreplacement 参数。它们使用 Java 的正则表达式语法。

  • regexp: 定义一个正则表达式来匹配原始路径。
  • replacement: 定义如何根据捕获组替换原始路径。${xxx} 是引用捕获组的方式。
示例 1:简单重写

原始路径: /api/v1/users/123
正则: /api/v1/(?<segment>.*)
替换: /${segment}
结果: /users/123

示例 2:多参数重写

原始路径: /api/v1/users/123/orders/456
正则: /api/v1/(?<resource>.*)/(?<id>.*)/(?<subResource>.*)/(?<subId>.*)
替换: /${resource}/${id}/${subResource}/${subId}
结果: /users/123/orders/456

示例 3:带参数的重写

原始路径: /api/v1/users?limit=10&offset=20
正则: /api/v1/(?<path>.*)
替换: /${path}
结果: /users?limit=10&offset=20 (注意:查询参数通常不会被 RewritePath 修改,除非特别处理)

2.2.5 注意事项
  • regexp 必须是一个有效的 Java 正则表达式。
  • replacement 中的 $\{groupName} 用于引用正则表达式中的命名捕获组。
  • 如果正则表达式不匹配,请求可能会被拒绝或按原路径转发。
  • RewritePath 只改变请求的路径部分,不影响查询参数(Query Parameters)和请求方法。

2.3 其他常用内置 Filter 🧰

除了上述两个核心 Filter,Spring Cloud Gateway 还提供了许多其他内置 Filter,这里简要介绍一些常用的:

2.3.1 SetStatus Filter ⚙️

用于设置 HTTP 响应的状态码。

spring:cloud:gateway:routes:-id: set-status-route uri: lb://some-service predicates:- Path=/status-test/**filters:-name: SetStatus args:status:404# 设置响应状态码为 404
2.3.2 AddResponseHeader Filter 📨

AddRequestHeader 类似,但它是在响应返回给客户端之前添加响应头。

spring:cloud:gateway:routes:-id: add-response-header-route uri: lb://some-service predicates:- Path=/response-header-test/**filters:-name: AddResponseHeader args:name: X-Response-Header value: ResponseValue 
2.3.3 StripPrefix Filter 🧼

移除请求路径开头的指定数量的路径段。

spring:cloud:gateway:routes:-id: strip-prefix-route uri: lb://some-service predicates:- Path=/api/v1/**filters:-name: StripPrefix args:parts:2# 移除前两个路径段,即 /api/v1/

例如,请求 /api/v1/users/123 会被处理为 /users/123

2.3.4 PrefixPath Filter 📌

在请求路径前添加一个前缀。

spring:cloud:gateway:routes:-id: prefix-path-route uri: lb://some-service predicates:- Path=/users/**filters:-name: PrefixPath args:prefix: /api/v1 # 在路径前添加 /api/v1

例如,请求 /users/123 会被处理为 /api/v1/users/123

三、实战演练:构建一个简单的网关服务 🛠️

为了更好地理解这些 Filter 的应用,我们将创建一个简单的 Spring Cloud Gateway 项目,并演示如何使用 AddRequestHeaderRewritePath

3.1 创建 Spring Boot 项目

首先,我们需要一个 Spring Boot 项目。你可以使用 Spring Initializr (🌐 https://start.spring.io/) 来初始化项目。

选择以下依赖项:

  • Spring WebFlux (注意:Gateway 基于 WebFlux)
  • Spring Cloud Gateway
  • Spring Boot Actuator (可选,用于健康检查)

3.2 项目结构

src/ └── main/ ├── java/ │ └── com/ │ └── example/ │ └── gatewaydemo/ │ ├── GatewayDemoApplication.java │ └── config/ │ └── GatewayConfig.java └── resources/ └── application.yml 

3.3 配置文件

创建 application.yml 文件:

server:port:8080spring:application:name: gateway-demo cloud:gateway:routes:# 示例 1: 使用 AddRequestHeader-id: header-test-route uri: https://httpbin.org # 使用 httpbin.org 作为测试服务predicates:- Path=/api/header-test/**filters:-name: AddRequestHeader args:name: X-Gateway-Source value: gateway-demo -name: AddRequestHeader args:name: X-Test-Value value: test-value-123# 示例 2: 使用 RewritePath-id: rewrite-test-route uri: https://httpbin.org # 使用 httpbin.org 作为测试服务predicates:- Path=/api/rewrite-test/**filters:-name: RewritePath args:regexp: /api/rewrite-test/(?<rest>.*)replacement: /$\{rest}# 示例 3: 结合使用-id: combined-test-route uri: https://httpbin.org # 使用 httpbin.org 作为测试服务predicates:- Path=/api/combined/**filters:-name: AddRequestHeader args:name: X-Combined-Header value: combined-value -name: RewritePath args:regexp: /api/combined/(?<rest>.*)replacement: /$\{rest}?source=combined management:endpoints:web:exposure:include:"*"endpoint:health:show-details: always 

3.4 主启动类

packagecom.example.gatewaydemo;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassGatewayDemoApplication{publicstaticvoidmain(String[] args){SpringApplication.run(GatewayDemoApplication.class, args);}}

3.5 配置类 (可选)

虽然我们使用了 YAML 配置,但也可以通过 Java 配置类来实现。创建 config/GatewayConfig.java

packagecom.example.gatewaydemo.config;importorg.springframework.cloud.gateway.route.RouteLocator;importorg.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassGatewayConfig{@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilder builder){return builder.routes()// 通过 Java 配置添加一个路由.route("java-configured-route", r -> r.path("/api/java-test/**").filters(f -> f.addRequestHeader("X-Java-Config","true").rewritePath("/api/java-test/(?<rest>.*)","/$\{rest}")).uri("https://httpbin.org"))// 测试服务.build();}}

3.6 启动项目

运行 GatewayDemoApplication 主类,确保项目成功启动。

3.7 测试效果

启动后,可以通过浏览器或 curl 工具来测试。

测试 AddRequestHeader

访问:http://localhost:8080/api/header-test/get

这个请求会被转发到 https://httpbin.org/get,并且会自动添加我们指定的请求头。

你可以通过访问 https://httpbin.org/headers 来查看请求头信息:

curl -X GET http://localhost:8080/api/header-test/get 

或者直接在浏览器中访问:https://httpbin.org/headers (请注意,这个链接是测试服务,不经过网关)。

测试 RewritePath

访问:http://localhost:8080/api/rewrite-test/post

这个请求路径会被重写,然后转发到 https://httpbin.org/post

curl -X POST http://localhost:8080/api/rewrite-test/post 
测试组合使用

访问:http://localhost:8080/api/combined/get

这个请求会同时添加请求头并重写路径。

curl -X GET http://localhost:8080/api/combined/get 

四、Filter 执行顺序 🔄

Spring Cloud Gateway 中的 Filter 执行顺序非常重要,因为它决定了请求和响应如何被处理。

4.1 Filter 的类型

Filter 分为两种类型:

  • Global Filters (全局过滤器): 对所有路由生效。
  • Route Filters (路由过滤器): 仅对特定路由生效。

4.2 执行顺序

Filter 的执行顺序遵循以下规则:

  1. 全局前置过滤器 (Global Pre Filters): 在路由匹配之前执行。
  2. 路由过滤器 (Route Filters): 针对特定路由的过滤器。
  3. 全局后置过滤器 (Global Post Filters): 在路由匹配之后执行。

你可以通过 org.springframework.core.Ordered 接口来控制 Filter 的执行顺序。数字越小优先级越高。

4.3 顺序示意图 (Mermaid 图表)

请求进入

全局前置过滤器

路由匹配

路由过滤器

目标服务

响应返回

全局后置过滤器

响应返回给客户端

4.4 自定义 Filter 顺序

如果你需要自定义某个 Filter 的顺序,可以实现 Ordered 接口:

importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.core.Ordered;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;@ComponentpublicclassCustomGatewayFilterimplementsGatewayFilter,Ordered{@OverridepublicMono<Void>filter(ServerWebExchange exchange,GatewayFilterChain chain){// 在这里执行自定义逻辑System.out.println("Custom filter executed before routing");return chain.filter(exchange).then(Mono.fromRunnable(()->{System.out.println("Custom filter executed after routing");}));}@OverridepublicintgetOrder(){// 返回一个数字,数字越小优先级越高return1;// 这个 Filter 将在全局前置过滤器之后执行}}

五、高级应用场景 💡

5.1 动态 Header 添加

AddRequestHeadervalue 参数支持 SpEL 表达式,这使得动态设置 Header 成为可能。

spring:cloud:gateway:routes:-id: dynamic-header-route uri: lb://service-a predicates:- Path=/api/dynamic/**filters:-name: AddRequestHeader args:name: X-Timestamp value:#{T(java.time.Instant).now().toEpochMilli()} # 使用 SpEL 获取当前时间戳-name: AddRequestHeader args:name: X-User-Agent value:#{request.headers['User-Agent']} # 从原始请求头中获取 User-Agent

5.2 复杂路径重写

对于复杂的路径重写需求,可以结合多个 RewritePath 或使用更高级的路由逻辑。

spring:cloud:gateway:routes:-id: complex-rewrite-route uri: lb://service-a predicates:- Path=/api/v3/users/*/orders/*filters:-name: RewritePath args:regexp: /api/v3/users/(?<userId>.*)/orders/(?<orderId>.*)replacement: /users/${userId}/orders/${orderId}

5.3 与认证服务集成

可以结合 AddRequestHeader 来传递认证信息。

spring:cloud:gateway:routes:-id: auth-integration-route uri: lb://protected-service predicates:- Path=/api/secure/**filters:-name: AddRequestHeader args:name: Authorization value: Bearer ${auth.token}# 从配置或上下文中获取 token

六、性能优化与最佳实践 🚀

6.1 合理使用 Filter

不是所有的 Filter 都是免费的。过多的 Filter 会增加请求处理时间。应该根据实际需求选择性地使用 Filter。

6.2 避免不必要的重写

RewritePath 虽然强大,但过度使用可能导致路径混乱,增加维护难度。尽量保持路径映射清晰简洁。

6.3 利用缓存和预热

对于频繁访问的路由,可以考虑使用缓存机制或提前预热网关实例,以提高响应速度。

6.4 监控与日志

为关键的 Filter 添加监控和日志记录,便于问题排查和性能分析。

七、总结 📝

Spring Cloud Gateway 的内置 Filter 是实现灵活路由和增强功能的强大工具。通过本文的介绍和实战演练,我们深入了解了 AddRequestHeaderRewritePath 的工作原理和使用方法。它们不仅简化了开发流程,还增强了系统的灵活性和可扩展性。

掌握这些 Filter 的使用,意味着你可以更有效地构建和管理微服务架构中的 API 网关。无论是简单的请求头注入还是复杂的路径重写,都能通过这些内置工具轻松实现。记住,合理的设计和配置是保证网关高效运行的关键。


参考链接:

提示: 本文中的代码示例仅供参考,实际部署时请根据具体环境调整配置和依赖。

祝你学习愉快! 🌟


🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Read more

【STL源码剖析】从源码看 list:从迭代器到算法

【STL源码剖析】从源码看 list:从迭代器到算法

半桔:个人主页  🔥 个人专栏: 《Linux手册》《手撕面试算法》《C++从入门到入土》 🔖源码之前,了不秘密。 文章目录 * 前言 * 一. list 概述 * 二. list 的节点 * 三. list 迭代器 * 3.1 定义 * 3.2 构造 * 3.3 重载 * 四. list 数据结构 * 五. list 的构造和内存管理 * 六. list 的接口 本文并不适合STL初学者。对于那些熟练掌握 C++ 模板和 STL 的日常使用,理解内存分配与对象生命周期,并且有扎实的数据结构基础,希望深刻了解STL实现细节,从而得以提升对STL的扩充能力,或是希望藉由观察STL源代码,学习世界一流程序员身手,

By Ne0inhk
【C++】STL之list模拟实现:关于链表容器的双向迭代器你知道多少?

【C++】STL之list模拟实现:关于链表容器的双向迭代器你知道多少?

前言: 前面的博客中我已经介绍了STL核心容器之一的list相关接口的使用,今天我们就从底层出发,来模拟实现一下list的那些核心接口函数。同时,也来感受一下list的双向迭代器到底与string和vector的随机迭代器有哪些区别? list容器功能接口介绍:https://blog.ZEEKLOG.net/Miun123/article/details/151685386?spm=1001.2014.3001.5502 废话不多说,我们直接进入今天的正题👇️👇️👇️ list容器深度剖析及模拟实现 我们想要模拟实现list容器,那就要理解list容器的底层结构。前面的博客已经提到,其本质就是一个双向链表,所以,成员变量就应该包含一个记录头节点的指针,以及记录有效节点个数的变量。同时,为了list容器可以满足不同类型的数据,我们将所有的类实现为类模板。 1、定义节点结构 struct创建的类默认所有的成员但是公开的,而节点结构就需要公开被list访问。 template<class T> struct list_node { // 成员变量 T _da

By Ne0inhk
排序--数据结构初阶(4)(C/C++)

排序--数据结构初阶(4)(C/C++)

文章目录 * 前言 * 理论部分: * 1.直接插入排序 * 2.希尔排序 * 3.直接选择排序 * 4.堆排序 * 5.冒泡排序 * 6.快速排序 * 归并排序 * 非比较排序 * 计数排序 * 作业部分 前言 这是数据结构初阶的最后一期,虽然来说在C++的库函数里面有sort函数可以代替这里所有的方法,并且时间复杂度也是优于他们的,但是sort函数是由他们写出来的,因此,还是是有必要学习一下的 理论部分: 这里的代码实现都是按升序来的 排序的话建议先写单趟再写整体 这些排序在两数相等的时候一般是不进行操作的(一般这么写) 1.直接插入排序 就是目前在最后的那个数跟前面每个数比,看看要插哪 时间复杂度:O(n2) 最好的情况下是O(n) 在小段小段有序时有极大的优势(相对于选择排序跟冒泡排序) 代码实现: void InsertSort(int* a, int n)

By Ne0inhk
大模型应用:最优路径规划实践:A*算法找最优解,大模型做自然语言解释.91

大模型应用:最优路径规划实践:A*算法找最优解,大模型做自然语言解释.91

一、引言         算法是个很有意义的课题,尽管大模型让我们不需要像以前学习机器学习那样,需要很深的数学基础,但结合算法来应用大模型确实是个很有趣的事情,传统算法经过数十年发展,已在路径规划、优化计算等领域达到极高的精确度;另一方面,大语言模型的崛起让人机交互变得前所未有的自然流畅。然而,一个不容忽视的现实是:再精确的算法,如果用户看不懂、不会用,就只是实验室里的玩具;再流畅的表达,如果缺乏技术可靠性,就只是华丽的空谈。         想象这样一个场景:导航系统为你计算出了一条理论上最优的路线,却只能用一串坐标告诉你怎么走;或者,一个能言善道的助手热情地为你指路,却把你带进了死胡同。这两种情况,本质上都是技术能力与用户体验之间的断裂。这种断裂并非偶然,而是单一技术路线的固有局限。算法擅长计算却不善表达,模型善于沟通却难以精确,各有所长,也各有所短。因此,真正的突破不在于让某一方变得更强大,而在于让两者形成互补协作的关系,用各自的长处弥补对方的短板。         这是我们今天想探讨得A*算法与大模型融合的核心价值所在。A*算法如同精密的"空间计算大脑",保证路径的数学最

By Ne0inhk