目录
- 1. Filter介绍
- 2. Filter类型
- 3. 内置过滤器
- 3.1 请求头(RequestHeader)相关GatewayFilter Factory
- 3.2 请求参数(RequestParameter)相关GatewayFilter Factory
- 3.3 回应头(ResponseHeader)相关GatewayFilter Factory
- 3.4 前缀和路径相关GatewayFilter Factory
- 3.5 Default Filters
- 4. 自定义过滤器
- 4.1 自定义全局过滤器
- 4.2 自定义单一内置过滤器
- 4.2.1 实现步骤:
- 4.2.2 实现代码如下:
- 4.2.3 application.yml配置
- 4.2.4 测试
1. Filter介绍
类似SpringMVC里面的的拦截器Interceptor,Servlet的过滤器。“pre”和“post”分别会在请求被执行前调用和被执行后调用,用来修改请求和响应信息。可以用来做请求鉴权、异常处理、记录接口调用时长
2. Filter类型
- 全局默认过滤器Global Filters:作用于所有的路由。gateway出厂默认已有的,直接用即可,不需要在配置文件中配置,实现GlobalFilter接口即可
- 单一内置过滤器GatewayFilter:也可以称为网关过滤器,这种过滤器主要是作用于单一路由或者某个路由分组
- 自定义过滤器
3. 内置过滤器
SpringCloud Gateway共提供了38种内置的GatewayFilter Factory。这里我们指讲解常用的
因为Request的相关操作,是一个前置过滤器,所以需要在最终提供服务的服务方Pay中实现如下controller,查看Request的详细情况
@GetMapping(value = "/pay/gateway/filter")public ResultData<String> getGatewayFilter(HttpServletRequest request){Enumeration<String> headerNames = request.getHeaderNames();while(headerNames.hasMoreElements()) {String headName = headerNames.nextElement();String headValue = request.getHeader(headName);System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);}Enumeration<String> parameterNames = request.getParameterNames();while(parameterNames.hasMoreElements()) {String parameterName = parameterNames.nextElement();String parameterValue = request.getParameter(parameterName);System.out.println("请求参数名: " + parameterName +"\t\t\t"+"请求参数值: " + parameterValue);}return ResultData.success(null);}
3.1 请求头(RequestHeader)相关GatewayFilter Factory
- AddRequestHeader GatewayFilter Factory:向请求头添加内容,需要两个参数,一个是请求头key ,一个是请求头value。如果请求头已经有配置文件的请求头key,则配置失效。gatewayServer的application配置如下。向网关发送
http://localhost:8088/pay/gateway/filter
请求,Pay的controller打印日志请求头名: headerkey 请求头值: headerValue
,表示网关的filter添加request header成功
spring:cloud:gateway:routes:- id: payRoute uri: lb://payment predicates:- Path=/pay/** filters:- AddRequestHeader=headerKey,headerValue
- RemoveRequestHeader GatewayFilter Factory:向请求头删除内容,需要一个参数,表示需要删除的请求头key
- 配置:
- RemoveRequestHeader=sec-fetch-site
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter
请求,Pay的controller没有打印sec-fetch-site
请求头的日志,表示网关的filter删除request header成功
- 配置:
- SetRequestHeader GatewayFilter Factory:修改请求头的内容,需要两个参数,一个是要修改的请求头key ,一个是要修改的请求头value。如果要修改的请求头key不存在,则进行添加
- 配置:
- SetRequestHeader=sec-fetch-mode,my-navigate
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter
请求,Pay的controller打印日志请求头名: sec-fetch-mode 请求头值: my-navigate
,表示网关的filter修改request header成功
- 配置:
3.2 请求参数(RequestParameter)相关GatewayFilter Factory
- AddRequestParameter GatewayFilter Factory:向请求参数添加内容,需要两个参数,一个是参数key ,一个是参数value。如果请求参数已经有配置文件的参数key,则配置失效
- 配置:
- AddRequestParameter=username,lily
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter
请求,Pay的controller打印日志请求参数名: username 请求参数值: lily
,表示网关的filter添加request parameter成功
- 配置:
- RemoveRequestParameter GatewayFilter Factory:向请求参数删除内容,需要一个参数,表示需要删除的参数key
- 配置:
- RemoveRequestParameter=userid
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter?userid=666
请求,Pay的controller没有打印userid
请求参数的日志,表示网关的filter删除request parameter成功
- 配置:
3.3 回应头(ResponseHeader)相关GatewayFilter Factory
-
AddResponseHeader GatewayFilter Factory:向响应头添加内容,需要两个参数,一个是响应头key ,一个是响应头value。如果配置文件的响应头key和已经有的响应头key一样,则配置继续有效
- 配置:
- AddResponseHeader=responseKey,responseValue
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter
请求,浏览器的Headers的Response Headers有responsekey: responseValue
,表示网关的filter添加response header成功
- 配置:
-
SetResponseHeader GatewayFilter Factory:修改响应头的内容,需要两个参数,一个是要修改的响应头key ,一个是要修改的响应头value。如果要修改的响应头key不存在,则进行添加
- 配置:
- SetResponseHeader=Date,2099-12-31
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter
请求,浏览器的Headers的Response Headers的Date为date: 2099-12-31
,表示网关的filter修改response header成功
- 配置:
-
RemoveResponseHeader GatewayFilter Factory:向响应头删除内容,需要一个参数,表示需要删除的响应头key
- 配置:
- RemoveResponseHeader=Content-Type
- 效果:向网关发送
http://localhost:8088/pay/gateway/filter
请求,浏览器的Headers的Response Headers没有Content-Type
这个响应header了,表示网关的filter删除response header成功
- 配置:
3.4 前缀和路径相关GatewayFilter Factory
- PrefixPath GatewayFilter Factory:给请求自动添加路径前缀。gatewayServer的配置文件配置如下。向网关发送
http://localhost:8088/gateway/filter
请求,符合predicates的要求,则通过filter给路径添加前缀/pay,最终请求的路径是payment的http://localhost:8001/pay/gateway/filter
。请求正常执行,表示网关的filter添加路径前缀成功
spring:cloud:gateway:routes:- id: payRoute uri: lb://payment predicates:- Path=/gateway/** filters:- PrefixPath=/pay
- SetPath GatewayFilter Factory:对请求路径进行修改。gatewayServer的配置文件配置如下。向网关发送
http://localhost:8088/pay2/gateway/filter
请求,符合predicates的要求,则通过filter修改路径为/pay/gateway/filter,最终请求的路径是payment的http://localhost:8001/pay/gateway/filter
。请求正常执行,表示网关的filter修改路径成功
spring:cloud:gateway:routes:- id: payRoute uri: lb://payment predicates:- Path=/pay2/{segment1}/{segment2} filters:- PrefixPath=/pay/{segment1}/{segment2} # 不支持通配符**。{segment}表示占位符,需要保持上下一致
- RedirectTo GatewayFilter Factory:将路径重定向到某个页面。gatewayServer的配置文件配置如下。向网关发送
http://localhost:8088/pay/gateway/filter
请求,符合predicates的要求,然后页面重定向打开的是百度的页面
spring:cloud:gateway:routes:- id: payRoute uri: lb://payment predicates:- Path=/pay/**filters:- RedirectTo=302, http://www.baidu.com
3.5 Default Filters
前面讲过的GatewayFilter Factory都可以使用,但作用于所有的route。gateway配置文件配置如下。向网关发送http://localhost:8088/pay/gateway/filter
请求,Pay的controller打印日志请求头名: headerkey 请求头值: headerValue
,表示网关的filter添加request header成功
spring:cloud:gateway:default-filters:- AddRequestHeader=headerKey,headerValueroutes:- id: payRoute uri: lb://payment predicates:- Path=/pay/**
4. 自定义过滤器
4.1 自定义全局过滤器
- 需求:统计每个请求的耗时时长
- 思路:不管是在每个接口中进行统计;还是通过AOP + 反射形成注解然后进行使用。都会对业务代码形成入侵,可以通过自定义全局过滤器来实现
- 步骤1:新建类MyGlobalFilter并实现GlobalFilter,Ordered两个接口。实现代码如下:
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 MyGlobalFilter implements GlobalFilter, Ordered {private static final String BEGIN_VISIT_TIME = "begin_visit_time";@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 先记录下访问接口的开始时间exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());// 进行放行,然后通过then获取到接口的响应return chain.filter(exchange).then(Mono.fromRunnable(() -> {Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);if (beginVisitTime != null) {System.out.println("接口主机: " + exchange.getRequest().getURI().getHost());System.out.println("接口端口: " + exchange.getRequest().getURI().getPort());System.out.println("接口URL: " + exchange.getRequest().getURI().getPath());System.out.println("接口URL参数: " + exchange.getRequest().getURI().getRawQuery());System.out.println("访问接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");System.out.println("###################################################");}}));}@Overridepublic int getOrder() {// 数字越小优先级越高return 0;}
}
接口主机: localhost
接口端口: 8088
接口URL: /pay/gateway/filter
接口URL参数: null
访问接口时长: 313ms
###################################################
4.2 自定义单一内置过滤器
4.2.1 实现步骤:
- 在gatewayServer项目中新建自定义Filter Factory类
MyGatewayFilterFactory
。类名的后缀必须是GatewayFilterFactory。所以这里创建的是一个My Gateway Filter - 类继承
AbstractGatewayFilterFactory
- 实现无参构造器,调用父类的构造器
- 实现一个配置类
MyGatewayFilterFactory.Config
,定义需要添加前缀的reponse header keyresponseHeaderKey
和给reponse header value添加的前缀responseHeaderPrefixValue
- 重写父类的
apply
方法,实现匹配reponse header的key和给reponse header的value添加前缀的逻辑 - 重写父类的
shortcutFieldOrder
方法,让其支持shortcut语法
4.2.2 实现代码如下:
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.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.Arrays;
import java.util.List;@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {public MyGatewayFilterFactory() {super(MyGatewayFilterFactory.Config.class);}public static class Config {@Getter@Setterprivate String responseHeaderKey; // 需要添加前缀的reponse header key@Getter@Setterprivate String responseHeaderPrefixValue; // 给reponse header value添加前缀}@Overridepublic GatewayFilter apply(MyGatewayFilterFactory.Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {return chain.filter(exchange).then(Mono.fromRunnable(() -> {// 获取reponse headersHttpHeaders responseHeaders = exchange.getResponse().getHeaders();responseHeaders.keySet().forEach(responseHeaderKey -> {// 如果reponse header的key和配置文件中的key相同,则给response hadear的value添加前缀if (responseHeaderKey.equals(config.getResponseHeaderKey())) {responseHeaders.set(responseHeaderKey, config.getResponseHeaderPrefixValue() + responseHeaders.getFirst(responseHeaderKey));}});}));// exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);// return exchange.getResponse().setComplete();}};}@Overridepublic List<String> shortcutFieldOrder() {// 列表: [responseHeaderKey, responseHeaderValue]return Arrays.asList("responseHeaderKey", "responseHeaderValue");}}
4.2.3 application.yml配置
全部展开语法配置如下:
- name: Myargs:responseHeaderKey: DateresponseHeaderPrefixValue: test_
快捷语法配置: - My=Date,test_
4.2.4 测试
向网关发送http://localhost:8088/pay/gateway/filter
请求。浏览器的Headers的Response Headers的Date为date: test_Mon, 12 Aug 2024 09:31:44 GMT
,表示自定义单一内置过滤器成功