网关过滤工厂

在上一篇文章中,主要是对网关进行了一个总体的介绍,然后对网关中的断言进行了一个描述。在这篇文章中,主要是对网关中的最后一大核心——过滤进行介绍。

当客户端发送过来的请求经过断言之后,如果还想在请求前后添加一些逻辑,那么就可以使用过滤器来做。这个过滤器其实就相当于SpringMVC中的拦截器、Servlet中的过滤器。同样,过滤和断言类似,SpringCloudGateway内部也内置了许多Filter。

过滤器可以分为全局默认过滤器(GlobalFilter)、单一内置过滤器(GatewayFilter)以及自定义过滤器。

过滤器可以在请求前后对业务逻辑进行处理,因此它的功能有:请求鉴权、记录接口耗时等。

GatewayFilter

GatewayFilter,表示单一内置过滤器,一般应用到单个路由或者一个分组的路由上。

和断言相同,在网关服务的配置文件中配置条件,然后在订单服务创建一个FilterController类来测试项目。

跟请求头相关的

AddRequestHeaderGatewayFilterFactory

该过滤器表示在请求头上添加内容,具体内容跟自己配置的文件相关。

如下配置表示在/filter/addRequestHeader请求的请求头上加上key为role,value为admin的内容。

spring:
  cloud:
    gateway:
      routes:
        - id: 11
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/addRequestHeader
          filters:
            - AddRequestHeader=role, admin
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * AddRequestHeaderGatewayFilterFactory
     * 表示在请求头中添加一些内容
     * 测试添加的是role=admin
     */
    @GetMapping("/addRequestHeader")
    public String addRequestHeader() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String role = request.getHeader("role");
        return "在过滤内置工厂中添加的内容为:role = " + role;
    }

}

RemoveRequestHeaderGatewayFilterFactory

改过滤器表示将请求头上的内容删除,具体删除内容跟配置文件有关。

如下配置表示移除请求头中key为content的内容。在postman的请求中,专门加入要移除的内容,测试之后发现请求头中确实没有,表示验证成功。

spring:
  cloud:
    gateway:
      routes:
        - id: 12
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/removeRequestHeader
          filters:
            - RemoveRequestHeader=content
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * RemoveRequestHeaderGatewayFilterFactory
     * 表示移除请求头中的一些内容
     * 测试移除的是content
     */
    @GetMapping("/removeRequestHeader")
    public String removeRequestHeader(HttpServletRequest request) {
        String value = "";
        Enumeration<String> headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            String headerKey = headers.nextElement();
            String headValue = request.getHeader(headerKey);
            value = value +  headerKey + '\t' + headValue + '\n';
        }
        return value;
    }

}

SetRequestHeaderGatewayFilterFactory

该过滤器表示修改请求头原先的一些设置,测试发现好像即使没加这个请求头,他也会自动加上。

如下配置表示将请求头中key为usernmae的内容,将其value设置成wbz。

spring:
  cloud:
    gateway:
      routes:
        - id: 13
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/setRequestHeader
          filters:
            - SetRequestHeader=username, wbz
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * SetRequestHeaderGatewayFilterFactory
     * 表示将原先请求头中的内容进行更换
     * 测试是将key为username的wky更换成wbz
     */
    @GetMapping("/setRequestHeader")
    public String setRequestHeader(HttpServletRequest request) {
        return "请求头中username对应的是:" + request.getHeader("username");
    }

}

跟请求参数相关的 

AddRequestParameterGatewayFilterFactory

该过滤器表示在请求参数上添加一些内容。

如下配置表示在请求参数上添加key为phone,value为13191038888的内容。

这里有一个值得注意的地方是,如果在过滤器中声明一个,然后自己写接口时再声明一个,那么使用参数接收到的就是两个内容,使用request.getParameter接收到的就是自己传过来的。

@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * AddRequestParameterGatewayFilterFactory
     * 表示在请求参数中加入一些内容
     * 测试是加入一个key为phone,value为1319103888的键值对
     */
    @GetMapping("/addRequestParameter")
    public String addRequestParameter(HttpServletRequest request, String phone) {
        log.info("测试" + phone);
        return "phone:" + request.getParameter("phone");
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: 14
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/addRequestParameter
          filters:
            - AddRequestParameter=phone,13191038888

RemoveRequestParameterGatewayFilterFactory

该过滤器表示删除请求参数上的一些内容。

如下配置表示删除请求参数上key为nickName的内容。

@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * RemoveRequestParameterGatewayFilterFactory
     * 表示在请求参数中删除一些内容
     * 测试是删除一个key为nickName,无论value是啥的内容
     */
    @GetMapping("removeRequestParameter")
    public String removeRequestParameter(HttpServletRequest request) {
        return "nickName:" + request.getParameter("nickName");
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: 15
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/removeRequestParameter
          filters:
            - RemoveRequestParameter=nickName

跟响应头相关的

AddResponseHeaderFilterGatewayFactory

该过滤器表示在响应头上添加一些内容。

如下配置表示在响应头上新增一个key为role,value为admin的键值对。

spring:
  cloud:
    gateway:
      routes:
        - id: 16
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/addResponseHeader
          filters:
            - AddResponseHeader=role, admin
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * AddResponseHeaderGatewayFilterFactory
     * 表示在响应头中新增一些内容
     * 测试是新增一个key为role,content为admin的键值对
     */
    @GetMapping("/addResponseHeader")
    public String addResponseHeader(HttpServletResponse response) {
        return "响应";
    }

}

RemoveResponseHeaderFilterGatewayFactory

该过滤器表示移除响应头上的一些内容。

如下配置表示将响应头中key为content的内容移除

spring:
  cloud:
    gateway:
      routes:
        - id: 17
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/removeResponseHeader
          filters:
            - RemoveResponseHeader=content
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * RemoveResponseHeaderGatewayFilterFactory
     * 表示在响应头中移除一些内容
     * 测试是将key为content的内容移除
     */
    @GetMapping("/removeResponseHeader")
    public String removeResponseHeader(HttpServletResponse response) {
        response.setHeader("content", "delete");
        return "响应";
    }

}

SetResponseHeaderFilterGatewayFactory

该过滤器表示设置响应头上的一些内容,即使原本响应头上没有,依旧可以设置成功,有的话就换成设置之后的。

如下配置表示将key为username的对应的value设置成wbz。

spring:
  cloud:
    gateway:
      routes:
        - id: 18
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/setResponseHeader
          filters:
            - SetResponseHeader=username, wbz
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * SetResponseHeaderGatewayFilterFactory
     * 表示在响应头中设置一些内容
     * 测试是将key为username的对应value设置为wbz
     */
    @GetMapping("/setResponseHeader")
    public String setResponseHeader(HttpServletResponse response) {
        response.setHeader("username", "wky");
        return "响应";
    }

}

跟路径有关的

PrefixPathGatewayFilterFactory

该过滤器是用来设置统一前缀的,假设某个服务的路径都带有一个相同的前缀,那么就可以使用该过滤器进行设置。当请求通过断言之后,过滤器路由服务之前就会自动加上前缀。

如下配置请求的完整URL是/filter/prefixPath,我将filter抽取出来放到过滤器中,因此我在输入URL时,只输入127.0.0.1:9527/prefixPath,就可以成功路由到/filter/prefixPath上。

spring:
  cloud:
    gateway:
      routes:
        - id: 19
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/prefixPath
          filters:
            - PrefixPath=/filter
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * PrefixPathGatewayFilterFactory
     * 表示设置一个响应统一前缀
     */
    @GetMapping("/prefixPath")
    public String prefixPath() {
        return "设置统一前缀成功";
    }

}

SetPathGatewayFilterFactory

该过滤器是用来进行地址替换的,客户端发送的地址经过断言之后,就会替换成过滤器中给出的真实地址。

如下配置是客户端发送的请求地址是127.0.0.1:9527/random/test,断言判断为true之后,过滤器就会将这个地址进行替换,然后再向服务端发送请求。

spring:
  cloud:
    gateway:
      routes:
        - id: 20
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/random/{regexp}
          filters:
            - SetPath=/filter/setPath/{regexp}
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * SetPathGatewayFilterFactory
     * 地址替换,即客户端发送的请求是根据断言来的
     * 断言成功之后过滤器会将其替换成真实的地址
     */
    @GetMapping("/setPath/{regexp}")
    public String setPath(@PathVariable String regexp) {
        return "地址替换成功";
    }

}

 RedirectToGatewayFilterFactory

该过滤器表示重定向。

如下配置表示当客户端访问127.0.0.1:9527/redirectTo时,网关就会重定向到百度。

spring:
  cloud:
    gateway:
      routes: 
       - id: 21
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/redirectTo
          filters:
            - RedirectTo=302,https://www.baidu.com

其他

RequestRateLimiterGatewayFilterFactory

该过滤器表示为当前网关的所有请求执行限流过滤,如果被限流,默认会响应HTTP 429-Too ManyRequests。默认提供了RedisRateLimiter的限流算法,采用令牌桶算法实现限流功能(对于具体的限流算法,在介绍Sentinel或其他限流组件时会详细介绍)。

如下配置表示限流过滤器的内容。

spring:
  cloud:
    gateway:
      routes: 
        - id: 22
          uri:
          predicates:
            - Path=/filter/**
          filters:
            - RequestRateLimiter=redis-rate-limiter.replenishRate, 10
            # 令牌填充速度,即每秒钟允许多少个请求(不丢弃任何请求)
            - RequestRateLimiter=redis-rate-limiter.burstCapacity, 20
            # 令牌桶容量,即每秒⽤⼾最⼤能够执⾏的请求数量(不丢弃任何请求)。将此值设置为零将阻⽌所有请求。
            - RequestRateLimiter=redis-rate-limiter.requestedTokens, 1
            # 每次请求占⽤⼏个令牌,默认为1。

RetryGatewayFilterFactory

该过滤器表示重试过滤器,会针对不同的响应进行重试。当后端服务不可用时,网关会根据配置参数来发起重试。

如下配置中,设置重试3次,然后针对BAD_GATEWAY的响应进行重试。BAD_GATEWAY是HttpStatus中的状态码,只要配置文件中配置的状态和服务响应回去的状态相同,那么就会进行重试。从结果可以看出,共发送了四次请求,正常发送一次,重试三次。

@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {

    /**
     * RetryGatewayFilterFactory
     * 重试过滤器
     */
    @GetMapping("/retry")
    public String retry(HttpServletResponse response) {
        log.info("重试机制");
        response.setStatus(502);
        return "重试机制";
    }

}
spring:
  cloud:
    gateway:
      routes:  
       - id: 23
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/retry
          filters:
            - name: Retry
              args:
                retries: 3
                statuses: BAD_GATEWAY

RequestSizeGatewayFilterFactory

该过滤器表示设置允许接收最大请求包的大小,如果请求大小超过设置的值,则返回413 Payload Too Large。请求包默认大小为5M,单位是字节。

spring:
  cloud:
    gateway:
      routes:  
        - id: 24
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/requestSize
          filters:
            - name: RequestSize
              args:
                maxSize: 2048 
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {
   
     /**
     * RequestSizeGatewayFilterFactory
     * 该过滤器表示能接收的请求包的大小,默认是5MB
     */
    @PostMapping("/requestSize")
    public String requestSize(@RequestPart MultipartFile file) {
        return "文件大小为:" + file.getSize();
    }

}

DefaultGatewayFilterFactory

前面写的都是针对某一路由生效的,若需要对全部路由生效,则可以使用该过滤器,这个过滤器就需要一个过滤列表,例如上述的重试过滤器、限流过滤器等等,就可以加入1到默认过滤器中,这样就可以对所有服务生效。

spring:
  cloud:
    gateway:
      default-filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
        - name: RequestSize
          args:
            maxSize: 2048

 GlobalFilter

GloablFilter表示全局过滤器,他和GatewayFilter的作用是相同的,只不过GlobalFilter会作用到所有的路由请求上。全局过滤器常用于实现与安全性、性能监控和日志记录等相关的全局功能。

全局过滤器Gateway中也内置有许多,例如:

GatewayMetricsFilter:网关指标,提供监控指标。

ForwardRoutingFilter:用于本地forword,请求不转发到下游服务器。

LoadBalancerClientFilter:针对下游服务,实现负载均衡。

具体内容见官网

自定义过滤器

自定义GatewayFilter

1. 继承AbstarctGatewayFilterFactory抽象类,并实现Orderd接口,而且自定义的类名必须以GatewayFilterFactory结尾。

2. 在自定义类内部新建一个Config类。

3. 重写apply方法,其中写的就是最重要的逻辑。

4. 空参构造方法,内部调用super。

5. 给Orderd赋值,值越小,优先级越高。

@Slf4j
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> implements Ordered {

    public MyGatewayFilterFactory() {
        super(MyGatewayFilterFactory.Config.class);
    }

    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("进入了自定义pre过滤器 + " + config.getName());
                ServerHttpRequest request = exchange.getRequest().mutate().headers((httpHeaders) -> {
                    httpHeaders.add("name", config.getName());
                }).build();
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    ServerHttpResponse response = exchange.getResponse();
                    response.getHeaders().set("role", "admin");
                    log.info("进入了自定义post过滤器");
                }));
            }
        };
    }

    @Override
    public int getOrder() {
        return 0;
    }

    public static class Config{

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

    }

}

测试:

如下配置中,表示在请求头中加入name:web的内容,在响应头中加入role:admin的内容,经过测试表明,想要加入的内容全部成功,证明自定义过滤器成功。

spring:
  cloud:
    gateway:
      routes:  
        - id: 25
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/filter/myGatewayFilter
          filters:
            - name: My
              args:
                name: wbz
@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {
    /**
     * 自定义的GatewayFilter
     * 在请求头上新增
     * 在响应头上新增
     */
    @GetMapping("/myGatewayFilter")
    public String myGatewayFilter(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerKey = headerNames.nextElement();
            String headerValue = request.getHeader(headerKey);
            log.info(headerKey + '\t' + headerValue);
        }
        return "成功";
    }

}

自定义GloablFilter

1. 实现GlobalFilter接口和Orderd接口。

2. 重写apply方法。

3. 自定义Orderd值,表示执行顺序。

@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put("begin", System.currentTimeMillis());
        
        return chain.filter(exchange).then((Mono.fromRunnable(() -> {
            Long begin = exchange.getAttribute("begin");
            if(begin != null) {
                log.info("接口的时间为:" + (System.currentTimeMillis() - begin));
            }
        })));
    }

    @Override
    public int getOrder() {
        return 0;
    }

}

启动项目之后随便找个接口进行测试,但是必须是走网关的接口,只要日志中出现下述内容就表示全局过滤器自定义成功。 

执行顺序

请求路由之后,会把该接口要经过的过滤器整合成一个链式结构,依次执行。每一个过滤器都有一个指定的order值,默认为0,表示该过滤器的优先级。值越小时,优先级越高,执行顺序越靠前。

假设order值相同时,按照默认过滤器 GatewayFilter GlobalFilter的顺序执行。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部