The Spring Cloud Gateway filter precisely controls the exception return (actual combat, fully customized return body)

Welcome to my GitHub

Here we classify and summarize all the original works of Xinchen (including supporting source code):

Overview of this article

  • As shown in the figure above, when an exception occurs, the system returns 8 fields, which is not flexible enough. In some scenarios with strict requirements on format and content, we need to be able to fully control the return code and the content of the return body. As shown below, only three fields are returned, and each field is fully used for business:
    # This is a return code with specific business meaning
    "code": "010020003",

    # This is a text message that accurately describes the cause of the error
    "message": "Make sure that the user-id Field is valid",
    # This is normal business data. When an exception occurs, this field is empty
    "data": null
  • Today, our goal is to customize the return information when an exception occurs through coding. The specific content is the above JSON data: there are only three fields: code, message and data

Source download

  • The complete source code in this actual combat can be downloaded from GitHub. The address and link information are shown in the table below(
name link remarks
Project Home The project is on the GitHub home page
git warehouse address (https) The warehouse address of the source code of the project, https protocol
git warehouse address (ssh) The project source code warehouse address, ssh protocol
  • There are multiple folders in this git project. The source code of this article is in the spring cloud tutorials folder, as shown in the red box below:

  • There are several sub projects under the spring cloud tutorials folder. The code of this chapter is gateway change body, as shown in the red box below:

Why not use conventional means

  • When it comes to global exception handling, experienced people should think of the global exception handling classes modified by the commonly used ControllerAdvice and ExceptionHandler annotations. However, the Spring Cloud Gateway is based on WebFlux. The HttpServletRequest we used to handle exceptions is not applicable in the Spring Cloud Gateway. Therefore, the means of ControllerAdvice and ExceptionHandler cannot be used To handle global exceptions

Basic ideas

  • Do enough theoretical analysis before you start, and the code you write can work normally

  • Open and find the renderErrorResponse method to see how the Spring Cloud Gateway originally constructs the exception return content:

  • At the moment, smart people should think of what to do: create a new class to inherit DefaultErrorWebExceptionHandler and override its renderErrorResponse method. In the new renderErrorResponse method, set the return content according to the actual business needs. Yes, that's our idea, but we need to refine it. The final specific steps are as follows:
  1. Add an exception class,, which has three fields: http return code, business return code and business description information

  2. At the code location where the exception is returned, use the customizeindexception class to throw an exception, and set the fields of the customizeindexception instance according to the actual business scenario

  3. is added, which inherits from DefaultErrorWebExceptionHandler and overrides the renderErrorResponse method, which checks whether the exception instance is of the type of customeindexception. If so, take out the http return code, business return code, business description information and other fields from it, and construct the content of the returned body. If the exception instance is not of the type of customeindexception Ion type, the previous processing logic remains unchanged;

  4. A new configuration class is added to register the MyErrorWebExceptionHandler instance with the spring environment

  • After analysis, start coding. For simplicity, this article will not add maven sub project, but based on Above Create the sub project gateway change body and continue to write code in it;


  • Add an exception class,
package com.bolingcavalry.changebody.exception;

import lombok.Data;
import org.springframework.http.HttpStatus;

public class CustomizeInfoException extends Exception {
     * http Return code
    private HttpStatus httpStatus;

     * body Code field in (business return code)
    private String code;

     * body Message field in (business return message)
    private String message;
  • Modify the apply method of, which is processing the request body. If it is found that there is no user ID field, the request will not be forwarded to the service provider provider provider Hello, but an error will be returned. The error here is handled with the customeindexception class:
    public Publisher<String> apply(ServerWebExchange exchange, String body) {
        try {
            Map<String, Object> map = objectMapper.readValue(body, Map.class);

            // If the request parameter does not contain user ID, an exception is returned
            if (!map.containsKey("user-id")) {
                CustomizeInfoException customizeInfoException = new CustomizeInfoException();
                // 406 is returned here. You can adjust it according to your business needs

                // Here, you can set your own code according to your business needs

                // Here, you can set the returned message according to your business needs
                customizeInfoException.setMessage("Make sure that the user-id Field is valid");

                return Mono.error(customizeInfoException);

            // Get id
            int userId = (Integer)map.get("user-id");

            // Write the map after obtaining the nanme
            map.put("user-name", mockUserName(userId));

            return Mono.just(objectMapper.writeValueAsString(map));
        } catch (Exception ex) {
            log.error("1. json process fail", ex);
            return Mono.error(new Exception("1. json process fail", ex));
  • For the exception handling class, one thing to pay attention to here is: the following code is only a reference. You don't need to stick to the logic related to customizeindexception, and you can freely set the returned status code and body according to business requirements:
package com.bolingcavalry.changebody.handler;

import com.bolingcavalry.changebody.exception.CustomizeInfoException;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;

public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties.Resources resources, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resources, errorProperties, applicationContext);

    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        // Return code
        int status;
        // Finally, the responseBodyMap is used to generate the response body
        Map<String, Object> responseBodyMap = new HashMap<>();

        // Like the parent class, get all exception information sorted out by DefaultErrorAttributes
        Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));

        // The original exception information can be obtained by getError method
        Throwable throwable = getError(request);

        // If the exception class is customized by us, we can customize it
        if (throwable instanceof CustomizeInfoException) {
            CustomizeInfoException myGatewayException = (CustomizeInfoException) throwable;
            // The http return code, the code field of the body, and the message field of the body are all obtained from the customeindexception instance
            status = myGatewayException.getHttpStatus().value();
            responseBodyMap.put("code", myGatewayException.getCode());
            responseBodyMap.put("message", myGatewayException.getMessage());
            responseBodyMap.put("data", null);
        } else {
            // If it is not our custom exception, maintain the same logic as the parent class
            // Return code
            status = getHttpStatus(error);
            // body content

        return ServerResponse
                // http return code
                // The type is the same as before
                // Content of response body
  • Finally, the configuration class
package com.bolingcavalry.changebody.config;

import com.bolingcavalry.changebody.handler.MyErrorWebExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

@Configuration(proxyBeanMethods = false)
public class MyErrorWebFluxAutoConfiguration {

    private final ServerProperties serverProperties;

    public MyErrorWebFluxAutoConfiguration(ServerProperties serverProperties) {
        this.serverProperties = serverProperties;

    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,
                                                             org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
                                                             WebProperties webProperties, ObjectProvider<ViewResolver> viewResolvers,
                                                             ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {

        MyErrorWebExceptionHandler exceptionHandler = new MyErrorWebExceptionHandler(errorAttributes,
                resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources(),
                this.serverProperties.getError(), applicationContext);
        return exceptionHandler;
  • After coding, it's time to run the program to verify the effect;


  • Start the application gateway change body

  • Send a POST request with postman. The address is http://localhost:8081/hello/change As shown in the following figure, the http return code in red box 2 is set in our code, and the returned content in red box 3 is the three fields we customized:

  • So far, the actual combat of controlling the abnormal return of Spring Cloud Gateway application has been completed. From the source code analysis and combined with the actual combat drill, I hope Xinchen's article can accompany you to deeply understand Spring Cloud Gateway and create a more powerful gateway application;

You're not alone. Xinchen's original accompanies you all the way

  1. Java series
  2. Spring collection
  3. Docker series
  4. kubernetes series
  5. Database + middleware series
  6. DevOps series

Welcome to the official account: programmer Xin Chen

Wechat search "programmer Xinchen", I'm Xinchen, looking forward to traveling with you in the Java World

Posted by rrn on Wed, 01 Dec 2021 09:14:19 -0800