目录

Spring Cloud Gateway

Spring Cloud Gateway 建立在Spring Boot 2.x、Spring WebFlux和Project Reactor之上。因此,当您使用 Spring Cloud Gateway 时,您所知道的许多熟悉的同步库(例如 Spring Data 和 Spring Security)和模式可能并不适用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<properties>
  <spring-cloud.version>2021.0.4</spring-cloud.version>    
</properties>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependencyManagement>
  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

词汇表

Predicate 网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由。

Predicate 这是一个Java 8 函数谓词。输入类型是 Spring Framework ServerWebExchange。这使您可以匹配来自 HTTP 请求的任何内容,例如标头或参数。

Filter 使用特定工厂构建的 GatewayFilter 实例。在这里,您可以在发送下游请求之前或之后修改请求和响应。

/images/spring-cloud/gateway/spring_cloud_gateway_diagram.png
a high-level overview of how Spring Cloud Gateway works

路由

StripPrefix参数表示在将请求发送到下游之前从请求中剥离的路径个数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
spring:
  cloud:
    gateway:
      routes:
        - id: route-svc-user
          uri: lb://svc-user  # 服务注册ID
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix=1

GlobalFilter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Bean
public GlobalFilter customFilter() {
    return new CustomGlobalFilter();
}

public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }

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

网关全局异常处理 实现 ErrorWebExceptionHandler

Spring Cloud Gateway 常见的方法有 实现自己的 DefaultErrorWebExceptionHandler 或 仅实现 ErrorAttributes。

重写 ErrorWebExceptionHandler#getRoutingFunction:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Component
public class GlobalErrorAttributes extends DefaultErrorAttributes {

  @Override
  public Map<String, Object> getErrorAttributes(
      ServerRequest request, ErrorAttributeOptions options) {
    Throwable error = super.getError(request);

    Map<String, Object> map = super.getErrorAttributes(request, options);
    map.put("message", error.getMessage());
    return map;
  }
}
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

  public GlobalErrorWebExceptionHandler(
      GlobalErrorAttributes gea,
      ApplicationContext applicationContext,
      ServerCodecConfigurer serverCodecConfigurer) {
    super(gea, new WebProperties.Resources(), applicationContext);
    super.setMessageWriters(serverCodecConfigurer.getWriters());
    super.setMessageReaders(serverCodecConfigurer.getReaders());
  }

  /**
   * 渲染html或json
   *
   * @param errorAttributes
   * @return
   */
  @Override
  protected RouterFunction<ServerResponse> getRoutingFunction(
      final ErrorAttributes errorAttributes) {
    return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
  }

  private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {

    final Map<String, Object> errorPropertiesMap =
        getErrorAttributes(request, ErrorAttributeOptions.defaults());

    return ServerResponse.status(HttpStatus.BAD_REQUEST)
        .contentType(MediaType.APPLICATION_JSON)
        .body(BodyInserters.fromValue(errorPropertiesMap));
  }
}

重写 ErrorWebExceptionHandler#handle:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Order(-1)
@Configuration
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
  private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);

  private ObjectMapper objectMapper;

  @Autowired
  public void setObjectMapper(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  @Override
  public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    ServerHttpResponse response = exchange.getResponse();

    if (exchange.getResponse().isCommitted()) {
      return Mono.error(ex);
    }

    String msg;

    if (ex instanceof NotFoundException) {
      msg = "服务未找到";
    } else if (ex instanceof ResponseStatusException) {
      ResponseStatusException responseStatusException = (ResponseStatusException) ex;
      msg = responseStatusException.getMessage();
    } else {
      // "内部服务器错误"
      msg = ex.getLocalizedMessage();
    }

    log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());

    String body;
    try {
      body = objectMapper.writeValueAsString(R.error(BaseResultCode.ERROR, msg));
    } catch (JsonProcessingException e) {
      throw new BaseException(e);
    }
    return ServletUtil.webFluxResponseWriter(response, body);
  }
}

对网关服务进行配置安全配置,由于 Gateway 使用的是WebFlux,所以需要使用 @EnableWebFluxSecurity 注解开启;

跨域设置

1
2
3
4
5
6
7
8
9
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
            - GET

附录