Spring Cloud Gateway:基础示例
Source: Dev.to

Spring Cloud Gateway(也称为网关或 Edge Service)是一个动态路由服务器。换句话说,它让我们能够为微服务提供一个集中式的访问入口。此外,我们还可以通过添加过滤器或谓词等方式来扩展其功能。
1. Spring Cloud Gateway 配置
使用 Spring Initializr(或您喜欢的 IDE)创建一个项目,并确保选择以下依赖:
- Eureka Discovery Client
- Gateway
该项目也会被 Eureka Server 注册,因此添加以下配置:
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
应用属性(YAML)
spring:
application:
name: gateway-server-service
server:
port: 8090
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
Source: …
2. 路由
Edge/Gateway 的最小必要配置是 路由。这些路由将请求从网关重定向到将响应请求的服务。
基本路由配置(YAML)
spring:
cloud:
gateway:
routes:
- id: employees
uri: lb://employees
predicates:
- Path=/api/employees/**
filters:
- StripPrefix=2
说明
| 元素 | 描述 |
|---|---|
id | 路由的唯一标识符。 |
uri | 网关查找要重定向的服务的地址。使用 Eureka 等服务发现时必须以 lb(负载均衡)开头。 |
predicates | 必须满足的重定向规则。 |
filters | 对通过声明路由的请求/响应进行操作的过滤器。 |
在上面的示例中,网关将请求路由到名为 employees 的微服务。要获取该服务的响应,只需调用网关的 Spring Cloud Gateway(端口 8090),而不是直接调用微服务的端口。
2.1 断言(Predicates)
断言是一组规则,只有满足这些规则才会成功重定向。它们可以基于路径、请求头、HTTP 方法、查询参数或 Cookie。
示例
predicates:
- Path=/api/products/**
- Header=token, \d+
- Method=GET, POST
- Query=color
- Cookie=color, blue
含义
| 断言 | 描述 |
|---|---|
Path | 重定向匹配声明路径的请求。 |
Header | 检查指定的请求头是否存在且其值匹配给定的正则表达式。 |
Method | 只允许列出的 HTTP 方法。 |
Query | 要求 URL 中必须包含给定的查询参数。 |
Cookie | 检查指定的 Cookie 是否存在(可选地匹配其值)。 |
2.2 Spring Cloud Gateway 工厂过滤器
工厂过滤器让你能够操作请求和响应(例如,添加请求头或请求参数)。
配置(YAML)
filters:
- AddRequestHeader=token-request, 123456
- AddResponseHeader=token-response, 12345678
- SetResponseHeader=Content-Type, text/plain
- AddRequestParameter=name, andres
这些过滤器仅在声明它们的路由上生效。
2.3 Spring Cloud Gateway 全局过滤器
全局过滤器会对 所有 通过网关的请求生效,无需在每条路由中单独声明。
示例实现
package com.funcionaenmimaquina.springboot.app.gateway.filters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@Component
public class ExampleGlobalFilter implements GlobalFilter, Ordered {
private final Logger logger = LoggerFactory.getLogger(ExampleGlobalFilter.class);
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("Executing request filter");
// Modifying the request
exchange.getRequest()
.mutate()
.header("environment", "dev")
.build();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
logger.info("Executing response filter");
// Adding a cookie to the response
exchange.getResponse()
.addCookie(ResponseCookie.from("myCookie", "myValue")
.path("/")
.maxAge(3600)
.build());
}));
}
@Override
public int getOrder() {
// Lower values have higher precedence
return -1;
}
}
在此示例中,过滤器会向每个传入请求添加一个 environment 头部,并向每个响应添加一个名为 myCookie 的 cookie。
全局过滤器示例(续)
o("Executing response filter");
// Modifying the response
Optional.ofNullable(exchange.getRequest().getHeaders().getFirst("environment"))
.ifPresent(value -> {
exchange.getResponse().getHeaders().add("env", value);
});
exchange.getResponse().getHeaders().setContentType(MediaType.TEXT_PLAIN);
}))
;
@Override
public int getOrder() {
return 1;
}
}
示例工作原理
- 实现了
GlobalFilter和Ordered接口。 getOrder定义了在存在多个全局过滤器时的执行顺序。filter方法包含所有与请求和响应交互的逻辑。- 第 24 行展示了可以在此处修改请求(例如,添加头部)。
- 第 27 行展示了如何使用
.then(Mono.fromRunnable(...))来修改响应。 - 第 31 行在响应中添加头部(前提是请求中存在特定头部)。
- 第 34 行强制响应使用纯文本(
text/plain)媒体类型。
2.4 过滤器工厂(自定义网关过滤器)
如果希望过滤器仅应用于选定的路由,可创建自定义过滤器工厂:
@Component
public class ExampleGatewayFilterFactory
extends AbstractGatewayFilterFactory {
private final Logger logger = LoggerFactory.getLogger(ExampleGatewayFilterFactory.class);
public ExampleGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
logger.info("Zone to modify the request");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
logger.info("Zone to modify the response");
Optional.ofNullable(config.cookieValue).ifPresent(cookieVal -> {
exchange.getResponse()
.addCookie(ResponseCookie.from(config.cookieName, cookieVal).build());
});
}));
};
}
@Override
public String name() {
return "ExampleCookie";
}
@Override
public List shortcutFieldOrder() {
return Arrays.asList("cookieName", "cookieValue");
}
public static class Config {
private String cookieName;
private String cookieValue;
// Getters and Setters
public String getCookieValue() { return cookieValue; }
public void setCookieValue(String cookieValue) { this.cookieValue = cookieValue; }
public String getCookieName() { return cookieName; }
public void setCookieName(String cookieName) { this.cookieName = cookieName; }
}
}
在 application.yml 中的使用方式
filters:
- StripPrefix=2
- ExampleCookie=user, myName
代码功能
- 使用 YAML 文件中定义的值向响应添加一个 cookie。
约定与要求
- 类名必须以
FilterFactory结尾。 - 需要继承
AbstractGatewayFilterFactory。 - 内部的
Config类用于存储从 YAML 文件传入的值。 - 过滤器逻辑位于
apply方法中。 name()方法的返回值必须与 YAML 中使用的键匹配(或默认使用类名前缀)。shortcutFieldOrder定义了短标记值映射到配置字段的顺序。
只有在 application.yml 中引用了此过滤器的路由才会应用它。
结论
您现在了解:
- 什么是 Spring Cloud Gateway 以及它解决的问题。
- 如何配置 predicates(谓词)和 filters(过滤器)以扩展其功能。
- 如何创建 global filters(全局过滤器)和 custom filter factories(自定义过滤器工厂)以实现细粒度控制。
练习构建和定制网关,熟练掌握在实际项目中使用它们。
