SpringCloud 2023 Gateway的Filter配置介绍、类型、内置过滤器、自定义全局和单一内置过滤器

1. Filter介绍

类似SpringMVC里面的的拦截器Interceptor,Servlet的过滤器。“pre”和“post”分别会在请求被执行前调用和被执行后调用,用来修改请求和响应信息。可以用来做请求鉴权、异常处理、记录接口调用时长

2. Filter类型

  1. 全局默认过滤器Global Filters:作用于所有的路由。gateway出厂默认已有的,直接用即可,不需要在配置文件中配置,实现GlobalFilter接口即可
  2. 单一内置过滤器GatewayFilter:也可以称为网关过滤器,这种过滤器主要是作用于单一路由或者某个路由分组
  3. 自定义过滤器

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,headerValue
      routes:
        - 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";

    @Override
    public 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("###################################################");
            }
        }));
    }

    @Override
    public int getOrder() {
        // 数字越小优先级越高
        return 0;
    }
}

  • 步骤2:无需配置application.yml
  • 测试:向网关发送http://localhost:8088/pay/gateway/filter请求,gatewayServer打印日志如下:
接口主机: localhost
接口端口: 8088
接口URL: /pay/gateway/filter
接口URL参数: null
访问接口时长: 313ms
###################################################

4.2 自定义单一内置过滤器

4.2.1 实现步骤:

  1. 在gatewayServer项目中新建自定义Filter Factory类MyGatewayFilterFactory。类名的后缀必须是GatewayFilterFactory。所以这里创建的是一个My Gateway Filter
  2. 类继承AbstractGatewayFilterFactory
  3. 实现无参构造器,调用父类的构造器
  4. 实现一个配置类MyGatewayFilterFactory.Config,定义需要添加前缀的reponse header key responseHeaderKey和给reponse header value添加的前缀responseHeaderPrefixValue
  5. 重写父类的apply方法,实现匹配reponse header的key和给reponse header的value添加前缀的逻辑
  6. 重写父类的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
        @Setter
        private String responseHeaderKey;      // 需要添加前缀的reponse header key

        @Getter
        @Setter
        private String responseHeaderPrefixValue;  // 给reponse header value添加前缀
    }

    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    // 获取reponse headers
                    HttpHeaders 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();
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        // 列表: [responseHeaderKey, responseHeaderValue]
        return Arrays.asList("responseHeaderKey", "responseHeaderValue");
    }


}

4.2.3 application.yml配置

全部展开语法配置如下:

            - name: My
              args:
                responseHeaderKey: Date
                responseHeaderPrefixValue: 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,表示自定义单一内置过滤器成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值