07-SpringCloud-Gateway新一代网关
一、概述
1、Gateway介绍
官网:https://spring.io/projects/spring-cloud-gateway
Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。 Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从而加强安全保护。Spring Cloud Gateway本身也是一个微服务,需要注册进服务注册中心。
作用:
- 反向代理
- 鉴权
- 流量监控
- 熔断
- 日志监控
2、Gateway三大核心
- Route(路由):路由是构建网关的基本模块,它由ID,目标URL,一系列的断言和过滤器组成,如果断言为true则匹配该路由;
- Predicate(断言):(java.util.function.Predicate)开发人员可以匹配HTTP请求中的所有内容(请求头和请求体),如果请求与断言相匹配则进行路由;
- Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后进行修改。
web前端请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
predicate就是我们的匹配条件;
filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。
3、Gateway工作流程
核心逻辑:路由转发+断言判断+执行过滤链
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(Post)执行业务逻辑。
在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
二、入门配置
1、建Module
新建 cloud-gateway9527 模块
2、改POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.atguigu.cloud</groupId>
<artifactId>cloud2024</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-gateway9527</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、写YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
4、主启动
package com.atguigu.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //服务注册与发现
public class Main9527 {
public static void main(String[] args) {
SpringApplication.run(Main9527.class, args);
}
}
5、测试
三、路由映射
不暴露8001端口,希望在8001真正的支付微服务外面套一层9527网关
1、改造8001模块
新建PayGateWayController
package com.atguigu.cloud.controller;
import cn.hutool.core.util.IdUtil;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.resp.ResultData;
import com.atguigu.cloud.service.PayService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName PayGateWayController
* @Author link
* @Date: 2024/11/6 下午3:56
* @Version v1.0
* @Description:
*/
@RestController
public class PayGateWayController {
@Resource
PayService payService;
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData<Pay> getById(@PathVariable("id") Integer id)
{
Pay pay = payService.getById(id);
return ResultData.success(pay);
}
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo()
{
return ResultData.success("gateway info test:"+ IdUtil.simpleUUID());
}
}
2、9527模块YML配置
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
3、修改公共api模块
package com.atguigu.cloud.apis;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.entities.PayDTO;
import com.atguigu.cloud.resp.ResultData;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @ClassName PayFeignApi
* @description: 通用api
* @author: linkai
* @create: 2024-11-03 15:29
**/
@FeignClient(value = "cloud-payment-service")
public interface PayFeignApi {
@PostMapping(value = "/pay/add")
@Operation(summary = "新增",description = "新增支付流水方法")
public ResultData addPay(@org.springframework.web.bind.annotation.RequestBody PayDTO payDTO);
@DeleteMapping(value = "/pay/del/{id}")
@Operation(summary = "删除",description = "删除支付流水方法")
public ResultData deletePay(@PathVariable("id") Integer id);
@PutMapping(value = "/pay/update")
@Operation(summary = "修改",description = "修改支付流水方法")
public ResultData updatePay(@RequestBody PayDTO payDTO);
@GetMapping(value = "/pay/get/{id}")
@Operation(summary = "查询",description = "根据id查询支付流水方法")
public ResultData getPayById(@PathVariable("id") Integer id);
@GetMapping(value = "/pay/getPayList")
@Operation(summary = "查询全部",description = "查询全部支付流水方法")
public ResultData getPayList();
/**
* openfeign天然支持负载均衡演示
* @return
*/
@GetMapping(value = "/pay/get/info")
public String getInfoByConsul();
/***
* Resilience4j CircuitBreaker 的例子
**/
@GetMapping(value = "/pay/circuit/{id}")
public String myCircuit(@PathVariable("id") Integer id);
/***
* Resilience4j Bulkhead 的例子
**/
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id);
/***
* Resilience4j Ratelimit 的例子
**/
@GetMapping(value = "/pay/ratelimit/{id}")
public String myRatelimit(@PathVariable("id") Integer id);
/***
* Micrometer 链路追踪 的例子
**/
@GetMapping(value = "/pay/micrometer/{id}")
public String myMicrometer(@PathVariable("id") Integer id);
/**
* Gateway 网关
**/
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData getById(@PathVariable("id") Integer id);
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo();
}
4、修改feign-order80模块
新建OrderGateWayController
package com.atguigu.cloud.controller;
import com.atguigu.cloud.apis.PayFeignApi;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.resp.ResultData;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName OrderGateWayController
* @Author link
* @Date: 2024/11/6 下午4:10
* @Version v1.0
* @Description:
*/
@RestController
public class OrderGateWayController {
@Resource
private PayFeignApi payFeignApi;
@GetMapping(value = "/feign/pay/gateway/get/{id}")
public ResultData getById(@PathVariable("id") Integer id)
{
return payFeignApi.getById(id);
}
@GetMapping(value = "/feign/pay/gateway/info")
public ResultData<String> getGatewayInfo()
{
return payFeignApi.getGatewayInfo();
}
}
5、测试
测试一:8001和9527访问
测试二:启动订单80服务,用80访问
但是这时候我们停掉网关也是一样可以访问,这是因为我们在PayFeignApi中定义通过找8001这个服务,需要改成网关服务
现在我们停掉网关:
开启网关:
四、Gateway高级特性
1、Route以微服务名-动态获取微服务URL
把uri的ip地址改成服务名称,如uri: lb: //cloud-payment-service
修改后的yml
xmserver:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
这样之后我们修改8001服务的端口,也不会受到影响
2、Predicate断言(谓语)
配置方式两种:
快捷方式配置
快捷方式配置由过滤器名称识别,后跟等号(=
),后跟用逗号分隔的参数值(,
)。
应用程序.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue
前面的示例定义了Cookie
Route Predicate Factory,它有两个参数:cookie 名称mycookie
和要匹配的值mycookievalue
。
充分展开配置
完全展开的参数看起来更像带有名称/值对的标准 yaml 配置。通常,会有一个name
键和一个args
键。args
键是用于配置谓词或过滤器的键值对的映射。
应用程序.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
1)常用的内置Route Predicate(2024.11月份最新 4.1.5版本)
以http://localhost:9527/pay/gateway/get/1地址为例,测试断言
1:After Request Predicate(请求后断言)
路由After
谓词工厂采用一个参数 a datetime
(即 java ZonedDateTime
)。此谓词匹配在指定日期时间之后发生的请求。以下示例配置了 after 路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
获得ZonedDateTime示例:
package com.atguigu.cloud.util;
import java.time.ZonedDateTime;
/**
* @ClassName CreateZoneDateTime
* @Author link
* @Date: 2024/11/7 上午8:51
* @Version v1.0
* @Description:
*/
public class CreateZoneDateTime {
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
}
}
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
#- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
2:Before Request Predicate(请求前断言)
路由Before
谓词工厂接受一个参数 a datetime
(即 java ZonedDateTime
)。此谓词匹配在指定 之前发生的请求datetime
。以下示例配置了一个 before 路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
#- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
3:Between Request Predicate(两者之间断言)
路由Between
谓词工厂接受两个参数,datetime1
和,datetime2
它们是 JavaZonedDateTime
对象。此谓词匹配发生在 之后datetime1
和 之前的请求datetime2
。datetime2
参数必须是 之后datetime1
。以下示例配置了一个 之间 路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
#- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
4:Cookie Request Predicate(Cookie请求断言)
路由Cookie
谓词工厂采用两个参数,cookiename
和 a regexp
(Java 正则表达式)。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。以下示例配置 cookie 路由谓词工厂:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
5:Header Request Predicate(标头请求断言)
路由Header
谓词工厂采用两个参数,即header
和 a regexp
(Java 正则表达式)。此谓词与具有给定名称且值与正则表达式匹配的标头匹配。以下示例配置标头路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
6:Host Request Predicate(主机请求断言)
路由Host
谓词工厂采用一个参数:主机名列表patterns
。该模式是 Ant 样式的模式,以.
作为分隔符。此谓词匹配与Host
模式匹配的标头。以下示例配置主机路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
7:Method Request Predicate(方法请求断言)
请求谓词Method
接受methods
一个或多个参数:要匹配的 HTTP 方法。以下示例配置方法路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
8:Path Request Predicate(路径请求断言)
Request Path
Predicate 接受两个参数:一个 Spring 列表PathPattern
patterns
。此 Request Predicate 使用RequestPredicates.path()
作为底层实现。以下示例配置路径路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
9:Query Request Predicate(查询请求断言)
路由Query
谓词工厂采用两个参数:一个必需参数param
和一个可选参数regexp
(Java 正则表达式)。以下示例配置查询路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Query=username, \d+
#- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
10:Weight Request Predicate(权重请求断言)
路由Weight
谓词工厂采用两个参数:group
和weight
(一个int
)。权重按组计算。以下示例配置权重路由谓词:
应用程序.yml
spring:
cloud:
gateway:
mvc:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
该路由将把80%的流量转发到weightthigh.org,20%的流量转发到weightlow.org。
修改9527YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Weight=group1, 2
#- Query=username, \d+
#- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- Weight=group1, 8
#- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
2)自定义断言,XXXRoutePredicateFactory规则
自定义模版:
- 继承AbstractRoutePredicateFactory抽象类
- 实现RoutePredicateFactory接口
- 开头任意取名,但是必须以RoutePredicateFactory后缀结尾
编写步骤
1:新建MyRoutePredicateFactory类并继承AbstractRoutePredicateFactory类
package com.atguigu.cloud.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
/**
* @ClassName MyRoutePredicateFactory
* @Author link
* @Date: 2024/11/7 上午10:10
* @Version v1.0
* @Description:
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
}
2:重写apply方法
package com.atguigu.cloud.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
/**
* @ClassName MyRoutePredicateFactory
* @Author link
* @Date: 2024/11/7 上午10:10
* @Version v1.0
* @Description:
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return null;
}
}
3:新建静态内部类Config方法
package com.atguigu.cloud.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
/**
* @ClassName MyRoutePredicateFactory
* @Author link
* @Date: 2024/11/7 上午10:10
* @Version v1.0
* @Description:
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return null;
}
@Validated
public static class Config {
@Getter
@Setter
@NotEmpty
private String userType;
}
}
4:空参构造方法,内部调用super
package com.atguigu.cloud.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
/**
* @ClassName MyRoutePredicateFactory
* @Author link
* @Date: 2024/11/7 上午10:10
* @Version v1.0
* @Description:
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return null;
}
@Validated
public static class Config {
@Getter
@Setter
@NotEmpty
private String userType;
}
}
5:重写apply方法第二版
package com.atguigu.cloud.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
/**
* @ClassName MyRoutePredicateFactory
* @Author link
* @Date: 2024/11/7 上午10:10
* @Version v1.0
* @Description:
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//检查request的参数里面,userType是否为指定值,符合配置通过
String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
if (userType == null) {
return false;
}
//如果参数存在则和config比较
if(userType.equals(config.getUserType())){
return true;
}
return false;
}
};
}
@Validated
public static class Config {
@Getter
@Setter
@NotEmpty
private String userType;
}
}
6:实现shortcutFieldOrder方法,使其支持短格式
package com.atguigu.cloud.config;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
/**
* @ClassName MyRoutePredicateFactory
* @Author link
* @Date: 2024/11/7 上午10:10
* @Version v1.0
* @Description:
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
//实现shortcutFieldOrder使其支持短格式写法
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("userType");
}
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//检查request的参数里面,userType是否为指定值,符合配置通过
String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
if (userType == null) {
return false;
}
//如果参数存在则和config比较
if(userType.equals(config.getUserType())){
return true;
}
return false;
}
};
}
@Validated
public static class Config {
@Getter
@Setter
@NotEmpty
private String userType;
}
}
7:配置yml
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
8:测试
3、Filter(过滤)
1)概述
过滤器分别会在请求被执行前调用或被执行后调用,用来修改请求和响应信息;
作用:
可以做请求鉴权,异常处理等;
类型:
- 全局默认过滤器Global Filters:gateway出厂默认已有的,直接使用即可,主要作用于所有路由,不需要在配置文件中配置,只需实现GlobalFilter接口即可;
- 单一内置过滤器GatewayFilter:也可以称为网关过滤器,这种过滤器主要作用于单一路由或者某个路由分组;
- 自定义过滤器。
2)Gateway内置过滤器
单一内置过滤器GatewayFilter
1:请求头(RequestHeader)相关组
The AddRequestHeader GatewayFilter Factory(指定请求头内容ByName)
新增8001模块PayGateWayController方法
package com.atguigu.cloud.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.resp.ResultData;
import com.atguigu.cloud.service.PayService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Enumeration;
/**
* @ClassName PayGateWayController
* @Author link
* @Date: 2024/11/6 下午3:56
* @Version v1.0
* @Description:
*/
@RestController
public class PayGateWayController {
@Resource
PayService payService;
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData<Pay> getById(@PathVariable("id") Integer id)
{
Pay pay = payService.getById(id);
return ResultData.success(pay);
}
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo()
{
return ResultData.success("gateway info test:"+ IdUtil.simpleUUID());
}
@GetMapping(value = "/pay/gateway/filter")
public ResultData<String> getGatewayFilter(HttpServletRequest request)
{
String result = "";
Enumeration<String> headers = request.getHeaderNames();
while(headers.hasMoreElements())
{
String headName = headers.nextElement();
String headValue = request.getHeader(headName);
System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
if(headName.equalsIgnoreCase("X-Request-atguigu1")
|| headName.equalsIgnoreCase("X-Request-atguigu2")) {
result = result+headName + "\t " + headValue +" ";
}
}
return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
}
}
修改9527yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
测试:
The RemoveRequestHeader GatewayFilter Factory (删除请求头Byname)
以删除sec-fetch-site为例
修改9527yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
测试:
The SetRequestHeader GatewayFilter Factory (修改请求头Byname)
以sec-fetch-mode为例:
修改9527yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
- SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
测试:
2:请求参数(RequestParameter)相关组
The AddRequestParameter GatewayFilter Factory(添加请求参数) 和 The RemoveRequestParameter GatewayFilter Factory(拦截请求参数)
修改9527yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
- SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
修改PayGateWayController方法
package com.atguigu.cloud.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.cloud.entities.Pay;
import com.atguigu.cloud.resp.ResultData;
import com.atguigu.cloud.service.PayService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Enumeration;
/**
* @ClassName PayGateWayController
* @Author link
* @Date: 2024/11/6 下午3:56
* @Version v1.0
* @Description:
*/
@RestController
public class PayGateWayController {
@Resource
PayService payService;
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData<Pay> getById(@PathVariable("id") Integer id)
{
Pay pay = payService.getById(id);
return ResultData.success(pay);
}
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo()
{
return ResultData.success("gateway info test:"+ IdUtil.simpleUUID());
}
@GetMapping(value = "/pay/gateway/filter")
public ResultData<String> getGatewayFilter(HttpServletRequest request)
{
String result = "";
Enumeration<String> headers = request.getHeaderNames();
while(headers.hasMoreElements())
{
String headName = headers.nextElement();
String headValue = request.getHeader(headName);
System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
if(headName.equalsIgnoreCase("X-Request-atguigu1")
|| headName.equalsIgnoreCase("X-Request-atguigu2")) {
result = result+headName + "\t " + headValue +" ";
}
}
//请求参数
System.out.println("=============================================");
String customerId = request.getParameter("customerId");
System.out.println("request Parameter customerId: "+customerId);
String customerName = request.getParameter("customerName");
System.out.println("request Parameter customerName: "+customerName);
System.out.println("=============================================");
return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
}
}
测试:
1、访问http://localhost:9527/pay/gateway/filter 测试添加请求参数
2、访问http://localhost:9527/pay/gateway/filter?customerId=99999&customerName=link
可以发现customerId的值会发生变化,customerName还是null,说明被拦截
3:回应头(ResponseHeader)相关组
The AddResponseHeader GatewayFilter Factory (增加响应头参数)、The SetResponseHeader GatewayFilter Factory(修改响应头参数)和The RemoveResponseHeader GatewayFilter Factory(删除响应头参数)
设置前浏览器响应头:
修改9527yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
- SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
- AddResponseHeader=X-Response-link, testResponse # 新增请求参数X-Response-link并设值为testResponse
- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
测试:
4:前缀和路径相关组
The PrefixPath GatewayFilter Factory (自动添加路径前缀)
之前正确访问地址:http://localhost:9527/pay/gateway/filter
修改之后访问地址:http://localhost:9527/gateway/filter
/pay前缀由过滤器统一管理
修改9527yml
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
# - Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
- Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
- SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
- AddResponseHeader=X-Response-link, testResponse # 新增请求参数X-Response-link并设值为testResponse
- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
- PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 隐藏的前缀
测试:
The SetPath GatewayFilter Factory (访问路径修改)
浏览器访问地址: http://localhost:9527/XYZ/abc/filter
实际微服务地址:http://localhost:9527/pay/gateway/filter
修改9527yml
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
# - Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
# - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
filters:
# - AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
# - AddRequestHeader=X-Request-atguigu2,atguiguValue2
# - RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
# - SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
# - AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
# - RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
# - AddResponseHeader=X-Response-link, testResponse # 新增请求参数X-Response-link并设值为testResponse
# - SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
# - RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
# - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 隐藏的前缀
- SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
测试:
The RedirectTo GatewayFilter Factory (重定向到某个页面)
修改9527yml
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
- My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
# - Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
# - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
#- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
- Path=/pay/gateway/filter/** # 真实地址
filters:
# - AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
# - AddRequestHeader=X-Request-atguigu2,atguiguValue2
# - RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
# - SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
# - AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
# - RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
# - AddResponseHeader=X-Response-link, testResponse # 新增请求参数X-Response-link并设值为testResponse
# - SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
# - RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
# - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 隐藏的前缀
# - SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
- RedirectTo=302, http://www.baidu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.baidu.com/
5:其他
Default Filters
配置在此处相当于全局通用,自定义秒变Global
spring:
cloud:
gateway:
default-filters:
-AddResponseHeader-X-Response-Default-Red,Default-Blue
-PrefixPath=/httpbin
3)Gateway自定义过滤器
1:自定义全局Filter
案例:统计接口调用耗时情况
自定义接口调用耗时统计全局过滤器
(1)新建类MyGlobalFilter并实现GlobalFilter,Ordered两个接口
package com.atguigu.cloud.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @ClassName MyGlobalFilter
* @Author link
* @Date: 2024/11/7 下午3:15
* @Version v1.0
* @Description:
*/
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return null;
}
@Override
public int getOrder() {
return 0;
}
}
(2)修改YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
# - My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
# - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
#- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
# - Path=/pay/gateway/filter/** # 真实地址
filters:
# - AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
# - AddRequestHeader=X-Request-atguigu2,atguiguValue2
# - RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
# - SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
# - AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
# - RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
# - AddResponseHeader=X-Response-link, testResponse # 新增请求参数X-Response-link并设值为testResponse
# - SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
# - RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
# - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 隐藏的前缀
# - SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
# - RedirectTo=302, http://www.baidu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.baidu.com/
(3)MyGlobalFilter 实现
package com.atguigu.cloud.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @ClassName MyGlobalFilter
* @Author link
* @Date: 2024/11/7 下午3:15
* @Version v1.0
* @Description:
*/
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
private static final String BEGIN_VISIT_TIME = "beginVisitTime";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//先记录下访问接口的时间
exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(()->{
Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
if(beginVisitTime != null){
log.info("访问接口主机: " + exchange.getRequest().getURI().getHost());
log.info("访问接口端口: " + exchange.getRequest().getURI().getPort());
log.info("访问接口URL: " + exchange.getRequest().getURI().getPath());
log.info("访问接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
log.info("访问接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");
log.info("我是美丽分割线: ###################################################");
System.out.println();
}
}));
}
/***
* 数字越小优先级越高
**/
@Override
public int getOrder() {
return 0;
}
}
测试:
分别访问:http://localhost:9527/pay/gateway/get/1
http://localhost:9527/pay/gateway/info
http://localhost:9527/pay/gateway/filter
2:自定义条件Filter
(1)新建类名MyGatewayFilterFactory需要以GatewayFilterFactory结尾并继承AbstractGatewayFilterFactory类
package com.atguigu.cloud.config;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName MyGatewayFilterFactory
* @Author link
* @Date: 2024/11/7 下午3:31
* @Version v1.0
* @Description:
*/
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
}
(2)新建MyGatewayFilterFactory.Config类
package com.atguigu.cloud.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName MyGatewayFilterFactory
* @Author link
* @Date: 2024/11/7 下午3:31
* @Version v1.0
* @Description:
*/
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
public static class Config{
@Setter
@Getter
private String status;
}
}
(3)重写apply方法
package com.atguigu.cloud.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @ClassName MyGatewayFilterFactory
* @Author link
* @Date: 2024/11/7 下午3:31
* @Version v1.0
* @Description:
*/
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status===="+config.getStatus());
if (request.getQueryParams().containsKey("link")){
return chain.filter(exchange);
}else {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
};
}
public static class Config{
@Setter
@Getter
private String status;
}
}
(4)重写shortcutFieldOrder
package com.atguigu.cloud.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName MyGatewayFilterFactory
* @Author link
* @Date: 2024/11/7 下午3:31
* @Version v1.0
* @Description:
*/
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
@Override
public List<String> shortcutFieldOrder() {
List<String> list = new ArrayList<String>();
list.add("status");
return list;
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status===="+config.getStatus());
if (request.getQueryParams().containsKey("link")){
return chain.filter(exchange);
}else {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
};
}
public static class Config{
@Setter
@Getter
private String status;
}
}
(5)空参构造方法,内部调用super
package com.atguigu.cloud.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName MyGatewayFilterFactory
* @Author link
* @Date: 2024/11/7 下午3:31
* @Version v1.0
* @Description:
*/
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
public MyGatewayFilterFactory() {
super(MyGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
List<String> list = new ArrayList<String>();
list.add("status");
return list;
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status===="+config.getStatus());
if (request.getQueryParams().containsKey("link")){
return chain.filter(exchange);
}else {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
};
}
public static class Config{
@Setter
@Getter
private String status;
}
}
(6)配置YML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
# - My=test
#- Weight=group1, 2
#- Query=username, \d+
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
#- Method=GET,POST
#- Host=**.atguigu.com
#- Header=X-Request-Id, \d+
# - Cookie=username,link
# - Between=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai], 2024-11-07T09:08:00.797682100+08:00[America/Denver]
#- Before=2024-11-07T09:05:00.797682100+08:00[America/Denver]
#- After=2024-11-07T08:58:38.797682100+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务名称
predicates:
#- Weight=group1, 8
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
# - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
#- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
# - Path=/pay/gateway/filter/** # 真实地址
filters:
- My=link
# - AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
# - AddRequestHeader=X-Request-atguigu2,atguiguValue2
# - RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
# - SetRequestHeader=sec-fetch-mode, Blue-updatebylink # 将请求头sec-fetch-mode对应的值修改为Blue-updatebylink
# - AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
# - RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
# - AddResponseHeader=X-Response-link, testResponse # 新增请求参数X-Response-link并设值为testResponse
# - SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
# - RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
# - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 隐藏的前缀
# - SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
# - RedirectTo=302, http://www.baidu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.baidu.com/
(7)测试