Gateway of Spring Cloud Alibaba series

One, foreword

Spring Cloud originally incorporated Zuul as a gateway component, which was provided by Netflix and is no longer maintained. Netflix later released a Zuul2.0 gateway, but since a stable version was never released, Spring Cloud couldn’t wait to release its own gateway and didn’t plan to integrate Zuul2.0.

Spring Cloud Gateway is a Gateway developed by Spring Based on Spring 5.0, Spring Boot 2.0 and Project Reactor technology. It aims to provide a simple and effective unified API route management approach for microservices architecture. It aims to replace Netflix Zuul, which not only provides a unified routing mode, but also provides basic gateway functions based on Filter chain, such as security, monitoring and traffic limiting.

Add: Spring Cloud Gateway does not belong to Spring Cloud Alibaba’s technology stack, so this chapter is added for the completeness of this series of microservices introduction.

2. Gateway Introduction

2.1 Core Concepts

  1. Routing: The basic construction component of a gateway, representing a specific routing information carrier. It is defined by the ID, target URI, predicate set, and filter set
  2. Predicate/Assertion: Java 8 function predicate, input type is Spring Framework ServerWebExchange, that matches everything in an HTTP request, such as request headers or parameters
  3. Filters: Instances of Spring Framework GatewayFilter constructed with a specific factory that modify requests and responses before or after they are sent to downstream requests

2.2 Execution Process

The execution process is as follows:

  1. The Gateway Client sends a request to the Gateway Server
  2. The request is first extracted by the HttpWebHandlerAdapter and assembled into the gateway context
  3. The context of the gateway then passed to the DispatcherHandler, it is responsible for will be distributed to RoutePredicateHandlerMapping request
  4. RoutePredicateHandlerMapping is responsible for routing lookup, and according to the routing assertion judgment routing is available
  5. If the assertion succeeds, the FilteringWebHandler creates the filter chain and calls it
  6. The request goes through the PreFilter -> Microservice -> PostFilter method once and the response is returned

3. Environment construction

To better understand the core concepts mentioned above, let’s use a simple case study.

The project name port describe
gateway-test Pom project, parent factory
user-service 9001 User microservices, services registered with nacOS
gateway-service 9090 Gateway services, services registered with NACOS

Note: Nacos service must be enabled before the construction project starts. Readers unfamiliar with Nacos can browse the articles by opening the portal first.

3.1 Set up the gateway-test project

The project is a POM project and only the following dependencies need to be added:

<packaging>pom</packaging>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5. RELEASE</version>
</parent>

<dependencyManagement>
    <dependencies>
        <! -- Spring Cloud dependencies -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <! -- Spring Cloud Alibaba -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.1. RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
Copy the code

3.2 Setting up a User-service Project

The project provides user-specific interfaces for user microservices, simulations.

  1. Add dependencies:
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
Copy the code
  1. Configuration file (application.yml):
server:
  port: 9001

spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
        username: nacos
        password: nacos
Copy the code
  1. Business class:
@Data
@AllArgsConstructor
public class User {

    private Integer id;

    private String name;
}
Copy the code
@RestController
@RequestMapping("/user")
public class UserController {

    private static Map<Integer, User> userMap;

    static {
        userMap = new HashMap<>();
        userMap.put(1.new User(1."Zhang"));
        userMap.put(2.new User(2."Bill"));
        userMap.put(3.new User(3."Fifty"));
    }

    @RequestMapping("/findById/{id}")
    public User findById(@PathVariable("id") Integer id) {
        // For testing convenience, use this method to simulate user query
        returnuserMap.get(id); }}Copy the code
  1. Start the class:
@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {

    public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); }}Copy the code

Start the user service, browser input: http://localhost:9001/user/findById/1, the results are as follows:

The user microservices are normal.

3.3 Setting up the Gateway-service Project

The gateway service provides the gateway function, the core of which is to configure routing rules.

  1. Add dependencies:
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>
Copy the code
  1. Configuration file (application.yml) :
server:
  port: 9090

spring:
  application:
    name: gateway-service

  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
        username: nacos
        password: nacos
    gateway:
      discovery:
        locator:
          enabled: true Gateway can discover microservices from nacOS
Copy the code

We do not configure routing rules yet.

  1. Start the class:
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); }}Copy the code

Starting the Gateway project, we try to request the user microservice interface through the gateway.

Request rule: Gateway address/micro service application name/interface

We in the browser input: http://localhost:9090/user-service/user/findById/2, the results are as follows:

The request is successful. The gateway project is set up.

Using routing rules:

server:
  port: 9090

spring:
  application:
    name: gateway-service

  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
        username: nacos
        password: nacos
    gateway:
      discovery:
        locator:
          enabled: true Gateway can discover microservices from nacOS
      routes:
        - id: user_service_route  # Route ID, determine the unique Garken
          uri: lb://user-service  # LB means to get microservices by name from NACOS, following load balancing
Balance policy, user-service Corresponding to the user micro-service application name
          predicates:
            - Path=/user-api/**  # use assertion
          filters:
            - StripPrefix=1       # Use filters
Copy the code

Among them:

  • Id: indicates the Route identifier, which is different from other routes
  • Uri: The destination URI to which the route points, that is, the microservice to which the client request is ultimately forwarded
  • Predicate: Predicate, used to determine conditions. The route is executed only when both assertions return true
  • Filter: Modifies the request and response information

Add routes configuration to restart the gateway project and request the user microservice interface.

Request rule: Gateway address/Path of assertion configuration/interface

We in the browser input: http://localhost:9090/user-api/user/findById/3, the results are as follows:

Routing rules take effect.

Routing rules are simply used. The following describes how to use routing rules.

Four, assertions

Predicate is used to determine conditions. The route is executed only when both assertions return true.

SpringCloud Gateway’s assertion through inheritance AbstractRoutePredicateFactory class implements, so we can according to their own requirements custom assertions.

Of course, the development team has provided consumers with some built-in assertion factories that are sufficient for use in development, so read on.

4.1 Built-in Assertions

The Spring Cloud Gateway includes 11 built-in assertion factories, all of which match different attributes of HTTP requests.

Add: Assertions can be used simultaneously

  1. AfterRoutePredicateFactory: receive a date parameter, to determine whether a request date later than the specified date
  2. BeforeRoutePredicateFactory: receive a date parameter, to determine whether a request date before the specified date
  3. BetweenRoutePredicateFactory: receiving date of two parameters, judge whether the request date within a specified period of time

The above three assertion factories are based on time. The usage is as follows:

predicates:
    - After = 2021-10-01 T00:00:00. 789 + 08:00 Asia/Shanghai
# 2021-08-01 - Between = T00:00:00) 789 + 08:00 Asia/Shanghai, the 2021-10-01 T00:00:00. 789 + 08:00 Asia/Shanghai
Copy the code

We set the interface to be accessed after October 01, 2021, and the current request time is August 12, 2021. The request result is as follows:

Failed to request the interface.

  1. CookieRoutePredicateFactory: accepts two parameters, the cookie name and value. Determines whether the requested cookie has the given name and the value matches the regular expression.
predicates:
    - Cookie=token, 123456
Copy the code

In the command, token indicates the cookie name and 123456 indicates the cookie value.

We can support the curl tool test, type in the curl http://localhost:9090/user-api/user/findById/3 — cookies token = 123456, the results are as follows:

  1. HeaderRoutePredicateFactory: accepts two parameters, the title name and regular expressions. Determines whether the request Header has the given name and the value matches the regular expression.
predicates:
    - Header=X-Request-Id, \d+
Copy the code

In the preceding command, x-request-id is the header name, \d+ is a regular expression, and a number.

We can support the curl tool test, type in the curl http://localhost:9090/user-api/user/findById/3 — header “X – Request – Id: 9527”, the results are as follows:

  1. HostRoutePredicateFactory: receive a parameter, the host name. Check whether the requested Host meets the matching rule.
predicates:
    - Host=**.somehost.org,**.anotherhost.org
Copy the code

URI template variables (such as {sub}.myhost.org) are supported. This route matches if the value of the requested host header is www.somehost.org or beta.somehost.org or www.anotherhost.org

  1. MethodRoutePredicateFactory: receive a parameter, to determine whether a request type with the specified type matching.
predicates:
    - Method=GET,POST
Copy the code

This route matches if the request method is GET or POST.

  1. PathRoutePredicateFactory: receive a parameter, judge whether the request URI part meet the path of rules.
predicates:
    - Path=/user-api/**
Copy the code

This is the assertion we configured above. Requests that start with /user-api/ are routed to the user microservice.

  1. Param QueryRoutePredicateFactory: accepts two parameters, request and regular expression, to determine whether a request parameter with the given name and value matches the regular expression.
predicates:
    - Query=cardId, \d+
Copy the code

If the request contains a parameter named cardId and its value is a number, the route is matched.

The tests are as follows:

  1. RemoteAddrRoutePredicateFactory: receive an IP address, to determine whether a request to the host address in the address section
predicates:
    - RemoteAddr = 192.168.0.1/16
Copy the code

192.168.0.1 is the IP address, and 16 is the subnet mask. When the remote address requested is this value, the route is matched.

  1. WeightRoutePredicateFactory: receive a/group name, weight, and then forward routing according to the weight. Within the same group.
spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
            - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
            - Weight=group1, 2
Copy the code

This parameter is used when configuring multi-group routing rules. The route forwards about 80% of traffic to Weighthigh.org and about 20% to weightlow.org.

4.2 Custom Assertion

When the built-in assertions do not meet our business needs, we can customize the assertion factory.

For example, we need to check the age value in the request URL in the range of 18 to 60 before normal routing.

  1. Configure assertions:
predicates:
    - Age=18, 60
Copy the code
  1. We need to create a class inherits AbstractRoutePredicateFactory class:

Note: Custom class names have format requirements -> Assertion name + RoutePredicateFactory. The assertion name is Age, which corresponds to Age in the configuration file.

@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    public AgeRoutePredicateFactory(a) {
        super(AgeRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder(a) {
        return Arrays.asList("minAge"."maxAge");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                // Determine the logic
                String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
                if (ageStr == null || ageStr.length() == 0) {
                    return false;
                }

                int age = Integer.parseInt(ageStr);
                returnage > config.getMinAge() && age < config.getMaxAge(); }}; }@Data
    static class Config {
        private int minAge;
        private intmaxAge; }}Copy the code
  1. Save and restart the gateway project. The test result is as follows:

Five, filter

Routing filters allow you to modify an incoming HTTP request or an outgoing HTTP response in some way.

In Gateway, the Filter has only two life cycles: “Pre” and “Post.”

  1. PRE: This filter is invoked before the request is routed. We can use this filter to authenticate, select requested microservices in the cluster, log debugging information, and so on
  2. POST: This filter is executed after routing to the microservice. Such filters can be used to add standard HTTP headers to responses, collect statistics and metrics, send responses from microservices to clients, and so on.

According to the scope of Filter, it can be divided into two types: GatewayFilter and GlobalFilter.

  • GatewayFilter: Applies to a single route or a group of routes.
  • GlobalFilter: Applies to all routes.

5.1 Local Filters

A local filter is a filter for a single route.

The Spring Cloud Gateway also provides 31 localized built-in GatewayFilter factories.

Due to the large number, the author only lists part of the built-in local filters for display.

Filter plant role parameter
AddRequestHeader Add the Header for the original request Header name and value
AddRequestParameter Add request parameters to the original request Parameter name and value
AddResponseHeader Add the Header for the original response Header name and value
DedupeResponseHeader Removes duplicate values from the response header The name of the Header to be deleted and the deduplication policy
PrefixPath Prefixes the original request path The prefix path
RequestRateLimiter It is used to limit the traffic of requests. The traffic limiting algorithm is token bucket KeyResolver, rateLimiter, statusCode, denyEmptyKey, emptyKeyStatus
RedirectTo Redirects the original request to the specified URL HTTP status code and redirected URL
StripPrefix The path used to truncate the original request Use numbers to indicate the number of paths to truncate
Retry Retry for different responses Retries, Statuses, Methods, series
ModifyRequestBody Modify the original request body content before forwarding the request Modified request body content
ModifyResponseBody Modify the contents of the original response body Modified response body content
SetStatus Modifies the status code of the original response The HTTP status code can be a number or a string

Usage:

spring:
    gateway:
      discovery:
        locator:
          enabled: true Gateway can discover microservices from nacOS
      routes:
        - id: user_service_route  
          uri: lb://user-service  
          predicates:
            - Path=/user-api/**  # use assertion
          filters:
            - StripPrefix=1 
            - SetStatus=2000  # Change the return status
Copy the code

Similarly, we can customize filters when the built-in local filters do not meet our business needs.

For example, we need to print a log before calling/routing an interface.

  1. Configuring local Filters
filters:
    - Log=true
Copy the code
  1. Create a class inherits AbstractGatewayFilterFactory class:

Note: Custom class name has format requirements -> filter name + GatewayFilterFactory. The filter name is Log, which corresponds to Log in the configuration file.

@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {


    public LogGatewayFilterFactory(a) {
        super(LogGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder(a) {
        return Arrays.asList("open");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if (config.open) {
                    // Filter logic processing
                    System.out.println("==== Enable log ====");
                }

                returnchain.filter(exchange); }}; }@Data
    static class Config {
        private booleanopen; }}Copy the code
  1. Save and restart the gateway project. The test result is as follows:

5.2 Global Filter

The global filter applies to all routes and does not need to be configured. Global filters can be used to verify permissions and security.

Similarly, the framework has some global filters built in, which implement the GlobalFilter and Ordered interfaces. Interested readers can check out GlobalFilter’s implementation classes for themselves or browse the official documentation provided below for details.

Here we mainly demonstrate custom global filters.

For example, we need to verify tokens when accepting requests.

Since this is a GlobalFilter, there is no need to modify the configuration file and classes need to be defined to implement the GlobalFilter and Ordered interfaces.

@Component
public class TokenGlobalFilter implements GlobalFilter.Ordered {

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.length() == 0| |! token.equals("123456")) {
            System.out.println("Authentication failed");
            ServerHttpResponse response = exchange.getResponse();

            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().add("Content-Type"."application/json; charset=UTF-8");

            // Data structure returned when authentication failed
            Map<String, Object> map = new HashMap<>();
            map.put("code", HttpStatus.UNAUTHORIZED.value());
            map.put("message", HttpStatus.UNAUTHORIZED.getReasonPhrase());

            DataBuffer buffer = response.bufferFactory().wrap(new ObjectMapper().writeValueAsBytes(map));
            return response.writeWith(Flux.just(buffer));
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder(a) {
        return 0; }}Copy the code

Save and restart the gateway project. The test result is as follows:

If the token authentication fails, a 401 message indicating authentication failure is displayed. The token authentication succeeds, and the interface result is displayed.

6. Route failure processing

When the request routing address does not match or the assertion is false, the Gateway returns a Whitelabel Error Page by default. This Error message does not meet our business requirements.

  1. We can custom to return to a more friendly error message, you need to create a class inherits DefaultErrorWebExceptionHandler class, rewrite the method:
public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
        Map<String, Object> errorMap = getErrorAttributes(request, includeStackTrace);
        int status = Integer.valueOf(errorMap.get("status").toString());
        Map<String, Object> response = this.response(status, errorMap.get("error").toString(), errorMap);
        return ServerResponse.status(status).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(response));
    }

    // The data structure we want to return
    public static Map<String, Object> response(int status, String errorMessage, Map<String, Object> errorMap) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", status);
        map.put("message", errorMessage);
        map.put("data", errorMap);
        returnmap; }}Copy the code
  1. Configure the Bean instance:
@Configuration
public class GatewayConfiguration {

    private final ServerProperties serverProperties;

    private final ApplicationContext applicationContext;

    private final ResourceProperties resourceProperties;

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties, ObjectProvider
       
        > viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer)
        {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }


    @Bean("myErrorWebExceptionHandler")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes) {

        MyErrorWebExceptionHandler exceptionHandler = new MyErrorWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext);

        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        returnexceptionHandler; }}Copy the code
  1. After saving, restart the gateway project, request an incorrect interface address, the result is as follows:

The requested URL does not match the routing rule returns an error we defined.

7. Cross-domain issues

For THE PC side of the page request, if the front and back end of the project is separated, the request will appear cross-domain request problem. Why is that? Then look at.

A URL consists of a protocol, domain name, port, and path. If the protocol, domain name, and port of two urls are the same, they are of the same origin.

Browsers provide same-origin policies that restrict documents or scripts from different sources to read or set certain properties on the current document. Its purpose is to ensure the security of user information and prevent malicious websites from stealing data.

The following author demonstrates cross-domain problems and writes a simple page:

<! DOCTYPEhtml>
<html lang="zh">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <button id="sendBtn">Send the request</button>
    <script src="jquery.min.js"></script>
    <script type="text/javascript">
        $(function() {$("#sendBtn").on("click".function() {
                $.ajax({
                    type: "GET".url: "http://localhost:9090/user-api/user/findById/3? token=123456".success: function(resp) {
                        console.log(resp); }})}); });</script>
</body>
</html>
Copy the code

Start a service container (I used sublime’s plug-in), assign port 10800, and request the following:

A cross-domain problem occurs because the port on the request side is not the same origin as the gateway port.

There are two solutions, as follows:

Method 1: Modify the configuration file

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/ * *]':
              allowedOrigins: "*"
              allowedMethods: "*"
              allowedHeaders: "*"
Copy the code

Method 2: Configure CorsWebFilter

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter(a) {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/ * *", config);
        return newCorsWebFilter(source); }}Copy the code

8. Sentinel integration

As a micro-service, the gateway can also be restricted and degraded. Readers unfamiliar with Sentinel can browse through the portal first.

Note: Remember to start the Sentinel console before configuration.

8.1 Basic Integration

  1. Add dependencies:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
    
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
Copy the code
  1. Modify the configuration file and connect to the Sentinel console:
Spring:
  Cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8081
Copy the code
  1. The Sentinel Filter instance was configured
@Configuration
public class GatewayConfiguration {

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter(a) {
        return newSentinelGatewayFilter(); }}Copy the code

Finally, restart the Gateway microservice to view or configure the rules on the Sentinel console.

8.2 Abnormal Processor

After the Sentinel Console is configured, when the service is curbed or degraded, we need the server to return a friendly exception message, not a simple error page.

In the previous article, we introduced the custom exception handler, which implements the BlockExceptionHandler interface to complete the function. However, when Gateway integrated Sentienl, the scheme was invalidated.

We need to configure the BlockRequestHandler instance.

@Configuration
public class GatewayConfiguration {

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter(a) {
        return new SentinelGatewayFilter();
    }

    @Bean(name = "myBlockRequestHandler")
    public BlockRequestHandler myBlockRequestHandler(a) {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @SneakyThrows
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {

                Result result;
                if (throwable instanceof FlowException) {
                    result = Result.builder().code(100).msg("The interface is restricted.").build();

                } else if (throwable instanceof DegradeException) {
                    result = Result.builder().code(101).msg("Service has been degraded.").build();

                } else if (throwable instanceof ParamFlowException) {
                    result = Result.builder().code(102).msg("Hot spot parameters are limited.").build();

                } else if (throwable instanceof SystemBlockException) {
                    result = Result.builder().code(103).msg("Triggering system protection rules").build();

                } else if (throwable instanceof AuthorityException) {
                    result = Result.builder().code(104).msg("Authorization rule failed").build();
                } else {
                    result = Result.builder().code(105).msg("Sentinel unknown Anomaly").build();
                }

                return ServerResponse.status(HttpStatus.BAD_GATEWAY)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(newObjectMapper().writeValueAsString(result))); }};return blockRequestHandler;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(BlockRequestHandler myBlockRequestHandler) {
    
        // Redirect bloack processing
        //GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("https://www.extlight.com"));
        
        // Custom bloack handling
        GatewayCallbackManager.setBlockHandler(myBlockRequestHandler);
        return newSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); }}Copy the code
@Data
@Builder
public class Result {

    private int code;

    private String msg;
}
Copy the code

Note: When the @ORDER annotation is configured on multiple beans, pay attention to the Order value, otherwise the interface request will not achieve the desired effect

Ix. Reference materials

The official documentation