SpringCloud Gateway 中过滤器中 GatewayFilter 介绍

2021-10-14 0 By admin

GatewayFilter 网关过滤器用于拦截并链式处理web请求,可以实现横切的与应用无关的需求,比如:安全、访问超时的设置等。

GatewayFilter 结构图
GatewayFilter 结构图

从类图中可以看到,GatewayFilter 有三个实现类:

  1. OrderedGatewayFilter 是一个有序的网关过滤器
  2. GatewayFilterAdapter 是一个适配器类,是web处理器(FilteringWebHandler)中的内部类
  3. ModifyResponseGatewayFilter 是一个内部类,用于修改响应体

一、GatewayFilter 接口定义

/**
 * Contract for interception-style, chained processing of Web requests that may
 * be used to implement cross-cutting, application-agnostic requirements such
 * as security, timeouts, and others. Specific to a Gateway
 */
public interface GatewayFilter extends ShortcutConfigurable {
  String NAME_KEY = "name";
  String VALUE_KEY = "value";
  /**
   *  过滤器执行方法
   * Process the Web request and (optionally) delegate to the next
   * {@code WebFilter} through the given {@link GatewayFilterChain}.
   * @param exchange the current server exchange
   * @param chain provides a way to delegate to the next filter
   * @return {@code Mono<Void>} to indicate when request processing is complete
   */
  Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

网关过滤器接口,有且只有一个方法filter,执行当前过滤器,并在此方法中决定过滤器链表是否继续往下执行。

二、网关过滤器的实现类

2.1、OrderedGatewayFilter 有序的网关过滤器

过滤器大多都是有优先级的,因此有序的网关过滤器的使用场景会很多。
在实现过滤器接口的同时,有序网关过滤器也实现了 Ordered 接口,构造函数中传入需要代理的网关过滤器以及优先级就可以构造一个有序的网关过滤器。
具体的过滤功能的实现在被代理的过滤器中实现的,因此在此只需要调用代理的过滤器即可。

/**
 * 排序的网关路由过滤器,用于包装真实的网关过滤器,已达到过滤器可排序
 */
public class OrderedGatewayFilter implements GatewayFilter, Ordered {
  //目标过滤器
  private final GatewayFilter delegate;
  //排序字段
  private final int order;
  public OrderedGatewayFilter(GatewayFilter delegate, int order) {
    this.delegate = delegate;
    this.order = order;
  }
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    return this.delegate.filter(exchange, chain);
  }
}

OrderedGatewayFilter实现类主要目的是为了将目标过滤器包装成可排序的对象类型,是目标过滤器的包装类。

2.2、GatewayFilterAdapter 网关过滤器适配器

在网关过滤器链 GatewayFilterChain 中会使用 GatewayFilter 过滤请求,GatewayFilterAdapter的作用就是将全局过滤器 GlobalFilter 适配成 网关过滤器 GatewayFilter。

//全局过滤器的包装类,将全局路由包装成统一的网关过滤器
private static class GatewayFilterAdapter implements GatewayFilter {
//全局过滤器
  private final GlobalFilter delegate;
  public GatewayFilterAdapter(GlobalFilter delegate) {
    this.delegate = delegate;
  }
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    return this.delegate.filter(exchange, chain);
  }
}

GatewayFilterAdapter实现类主要目的是为了将GlobalFilter过滤器包装成GatewayFilter类型的对应。是GlobalFilter过滤器的包装类。

2.3、ModifyResponseGatewayFilter 修改响应体的过滤器

ModifyResponseGatewayFilter 是 ModifyResponseBodyGatewayFilterFactory 的内部类,用于修改响应体的信息

public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {
  private final Config config;

  public ModifyResponseGatewayFilter(Config config) {
    this.config = config;
  }

  @Override
  @SuppressWarnings("unchecked")
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {

    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

      Class inClass = config.getInClass();
      Class outClass = config.getOutClass();

      String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
      HttpHeaders httpHeaders = new HttpHeaders();
      //explicitly add it in this way instead of 'httpHeaders.setContentType(originalResponseContentType)'
      //this will prevent exception in case of using non-standard media types like "Content-Type: image"
      httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType);
      ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders);
      DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());

      //TODO: flux or mono
      Mono modifiedBody = clientResponse.bodyToMono(inClass)
        .flatMap(originalBody -> config.rewriteFunction.apply(exchange, originalBody));

      BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
      CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders());
      return bodyInserter.insert(outputMessage, new BodyInserterContext())
        .then(Mono.defer(() -> {
        long contentLength1 = getDelegate().getHeaders().getContentLength();
        Flux<DataBuffer> messageBody = outputMessage.getBody();
        //TODO: if (inputStream instanceof Mono) {
          HttpHeaders headers = getDelegate().getHeaders();
          if (/*headers.getContentLength() < 0 &&*/ !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
          messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
          }
        // }
        //TODO: use isStreamingMediaType?
        return getDelegate().writeWith(messageBody);
        }));
    }

    @Override
    public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
      return writeWith(Flux.from(body)
        .flatMapSequential(p -> p));
    }
    };

    return chain.filter(exchange.mutate().response(responseDecorator).build());
  }

  @Override
  public int getOrder() {
    return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
  }
}