Welcome to my GitHub

Here classification and summary of xinchen all original (including supporting source code) : github.com/zq2599/blog…

This paper gives an overview of

  • In the article “Spring Cloud Gateway to modify the request and response body content”, we successfully modify the request body content through filter, then left a problem: If an exception occurs in filter (for example, the request parameter is illegal), when the exception information is thrown, the return code and body received by the caller are processed by the Spring Cloud Gateway framework, and the caller cannot know the real cause of the error according to these contents, as shown below:

  • The task of this article is to analyze the causes of this phenomenon by reading the source code to understand the logic behind the generation of the return code and response body

Summary in advance

  • Here is a summary of the analysis results in advance. If you are busy and don’t have much time but want to know the final cause, you can focus directly on the following summary:
  1. The Spring Cloud Gateway application has a bean of type ErrorAttributes whose getErrorAttributes method returns a map
  2. When a throw exception is applied, the return code comes from the status value of the map above, and the body is returned as the result of the entire map serialization
  3. ErrorAttributes’ implementation class by default is TerrorAttributes
  • Look again at the map’s status value (the return code for response), which was generated by TerrorAttributes:
  1. Let’s see if the exception object is of type ResponseStatusException
  2. If it is of type ResponseStatusException, the getStatus method of the exception object is called as the return value
  3. If it’s not ResponseStatusException, and if it’s ResponseStatus,
  4. If so, take the code attribute of the annotation as the return value
  5. If the exception object is neither ResponseStatusException nor ResponseStatus annotated, 500 is returned
  • Finally look at the Message field of the Map (the Message field of the Response Body) and how it was generated by the TerrorAttributes:
  1. The exception object is of type BindingResult
  2. If it’s not of type BindingResult, look at ResponseStatusException
  3. If so, use getReason as the return value
  4. If it is also not of type ResponseStatusException, see if the exception class has a ResponseStatus annotation. If it does, return the reason attribute of the annotation
  5. If reason obtained through the annotation is also invalid, the getMessage field of the exception is returned
  • That is the essence of this article, but it does not cover the analysis process. If you are interested in Spring Cloud source code, please allow Chen to accompany you on a short reading tour of the source code

Spring Cloud Gateway error handling source code

  • First to see the configuration class ErrorWebFluxAutoConfiguration. Java, registered for spring here two instances, each of which is very important, let’s focus on the first, That is, the implementation class ErrorWebExceptionHandler DefaultErrorWebExceptionHandler is:

  • Handle exceptions, through FluxOnErrorResume calls to the ErrorWebExceptionHandler handle method processing, the method in its parent class AbstractErrorWebExceptionHandler. In Java, as the chart, The code in the red box is key, and this is where the exception returns are determined:

  • Expand the getRoutingFunction method to see that renderErrorResponse is called to handle the response:
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
		return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
	}
Copy the code
  • Open the renderErrorResponse method, as shown below, and the truth is revealed!
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
  // Retrieve all error messages
  Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
  
  // Construct all the information returned
  return ServerResponse
           // Control the return code
           .status(getHttpStatus(error))
           // Control returns ContentType
           .contentType(MediaType.APPLICATION_JSON)
           // Control what is returned
           .body(BodyInserters.fromValue(error));
}
Copy the code
  • From the above code, we draw two important conclusions:
  1. The status code returned to the caller depends on the return value of the getHttpStatus method
  2. The body returned to the caller, depending on the contents of the error
  • Now that you’ve read this, it’s natural to look inside getHttpStatus, as shown below. Status comes from the input parameter:
protected int getHttpStatus(Map<String, Object> errorAttributes) {
  return (int) errorAttributes.get("status");
}
Copy the code
  • At this point we can conclude that the return value of the getErrorAttributes method is the key to determining the return code and return body!

  • To see the truth of the getErrorAttributes approach, in DefaultErrorAttributes. In Java (recall gave ErrorWebFluxAutoConfiguration. Java, have previously mentioned the contents are very important, Also includes the errorAttributes method) :

public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes = this.getErrorAttributes(request, options.isIncluded(Include.STACK_TRACE));
        if (Boolean.TRUE.equals(this.includeException)) {
            options = options.including(new Include[]{Include.EXCEPTION});
        }

        if(! options.isIncluded(Include.EXCEPTION)) { errorAttributes.remove("exception");
        }

        if(! options.isIncluded(Include.STACK_TRACE)) { errorAttributes.remove("trace");
        }

        if(! options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
            errorAttributes.put("message"."");
        }

        if(! options.isIncluded(Include.BINDING_ERRORS)) { errorAttributes.remove("errors");
        }

        return errorAttributes;
    }
Copy the code
  • I won’t expand the above code for space, so LET’s go straight to the result:
  1. The return code is from determineHttpStatus
  2. The message field is returned from determineMessage
  • Check the determineHttpStatus method.
private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        // The exception object is of type ResponseStatusException
        return error instanceof ResponseStatusException 
        // If it is of type ResponseStatusException, the getStatus method of the exception object is called as the return value
        ? ((ResponseStatusException)error).getStatus() 
        // If the exception class is not ResponseStatusException, and if the exception class is ResponseStatus,
        // If so, take the code attribute of the annotation as the return value
        : (HttpStatus)responseStatusAnnotation.getValue("code", HttpStatus.class)
        // If the exception object is neither ResponseStatusException nor ResponseStatus, return 500
        .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
    }
Copy the code
  • In addition, the contents of the message field are determined:
    private String determineMessage(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        // The exception object is of type BindingResult
        if (error instanceof BindingResult) {
            // If so, use getMessage as the return value
            return error.getMessage();
        } 
        // If not BindingResult, then ResponseStatusException
        else if (error instanceof ResponseStatusException) {
            // If so, use getReason as the return value
            return ((ResponseStatusException)error).getReason();
        } else {
            // If it is not ResponseStatusException,
            // If the exception class has a ResponseStatus annotation, take its Reason attribute as the return value
            String reason = (String)responseStatusAnnotation.getValue("reason", String.class).orElse("");
            if (StringUtils.hasText(reason)) {
                return reason;
            } else {
                // If the reason obtained through the annotation is also invalid, return the getMessage field of the exception
                returnerror.getMessage() ! =null ? error.getMessage() : ""; }}}Copy the code
  • So far, the source code analysis has been completed, the final return code and return content exactly how to control, I believe you should have a smart heart, the next “actual combat” we strike while the iron is hot, write code to try to accurately control the return code and return content

  • Reveal the plot in advance, the following “Actual Combat” will be presented:

  1. Straightforward, control the return code and the error field in the body
  2. A little bit of a roadblock
  3. Easy to use, annotated control of returned information
  4. The ultimate solution, complete customization of returned content
  • The above content please look forward to, Xinchen original will live up to you

You are not alone, Xinchen original accompany all the way

  1. Java series
  2. Spring series
  3. The Docker series
  4. Kubernetes series
  5. Database + middleware series
  6. The conversation series

Welcome to pay attention to the public number: programmer Xin Chen

Wechat search “programmer Xin Chen”, I am Xin Chen, looking forward to enjoying the Java world with you…

Github.com/zq2599/blog…