Analysis of error handling principle

In web projects created using SpringBoot, SpringBoot returns an error message when the requested page does not exist (HTTP status code 404) or when the server is abnormal (HTTP status code 500 is typical).

That is, in SpringBoot’s Web project, a /error error interface is automatically created to return error messages. However, there are two different types of return information for different access modes. This depends on the Accept value of the HTTP header you visit to specify what types you can receive

  • The header when accessed using a browser and the result returned
Accept: text/html
Copy the code

  • Header information and return results when accessed using other devices such as mobile clients (typically in a forward-to-back architecture)
Accept: */*
Copy the code

Second, error handling

There are two main ways to handle exceptions:

1. Use the SpringBoot automatic configuration principle to handle exceptions

SpringBoot automatically configured with a class ErrorMvcAutoConfiguration to handle handle exceptions, interested can go to look at, and then defined in the class a wrong BasicErrorController class, main code are as follows:

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {

  	/** * error page response */
    @RequestMapping(produces = {"text/html"})
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
      	// Get a modelAndView object
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        returnmodelAndView ! =null ? modelAndView : new ModelAndView("error", model);
    }
		
  /** * error json response */
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
            return newResponseEntity(body, status); }}}Copy the code

More code will not delve into, interested can go to see. This code says that different results will be returned for different requests, the key is in the @requestMapping produces = {“text/ HTML “} attribute

1) Return an error page, such as 404, 500, etc.

  • Have a template engine (can be used to render pages)

The project uses template engines such as Thymeleaf and Freemarker for page rendering. Go to the templates create /error folder and add the.html file for the error status code as shown below:

Here 404 and 500 are the definitive error status codes, while 4xx represents other 4-beginning errors, such as 400,401, etc. Of course, you can set up an error page for each status code, but there is no benefit in doing so, so use a generic name like 4xx.html instead.

You can get the following information from our error page (the contents of the ModelAndView object) :

The field name instructions
timstamp The time stamp
status Error status code
error Error message
exception The exception object
message The exception message
path Page path

If you are careful, you will notice that this is actually the JSON content returned when you request it from your mobile phone

For example, add the above information to your code and write an error code on the back end:

@RequestMapping("haserror")
@ResponseBody
public Object myError(a){
  int i =10/0;
  return "something is error";
}
Copy the code
This is an error page:<ul>
    <li>[${status}]]</li>
    <li>[${error}]]</li>
    <li>[[${exception}]]</li>
    <li>[[${message}]]</li>
    <li>[[${timestamp}]]</li>
</ul>
Copy the code

  • There is no template engine

When the template engine is not used in the project, you can simply move the entire Error folder to the static folder.

However, the above information is not available at this point because it is a static resource and there is no template engine to render it

2) Return the corresponding JSON string

There’s nothing to be said for this, it just returns a JSON string. The format is as follows:

{
"timestamp": "The 2020-04-22 T16:13:37. 506 + 0000"."status": 500."error": "Internal Server Error"."message": "/ by zero"."path": "/hello/haserror"."reason": "Oh, no, your code caused another online accident."
}
Copy the code

3) Customize the page return information

This is the most important thing, because this information is not only returned as JSON, it is available in the error page above, or it can be returned directly as json. It’s as simple as adding an ErrorAttributes object to the Spring container, which I’ve chosen to subclass.

@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        // Calling the parent method automatically gets those built-in attributes, so you don't have to call them if you don't want them
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);

        // Add custom attributes
        errorAttributes.put("reason"."Oh, no, your code caused another online accident.");
        // You can take a look at the webRequest object as an argument to this method. I'm sure you'll find something good

        returnerrorAttributes; }}Copy the code

There we go, testing our custom property in two separate requests:

2. Handle with AOP’s exception notification (recommended)

It works by getting a global exception notification and processing it. We only need to write the following code in the project (in fact, the above is just a custom exception information class)

@ControllerAdvice
public class ErrroAcvice {

    /** * Global catch exception section class *@paramRequest Indicates the request object. * is not transmitted@paramResponse Indicates the response object. * is not transmitted@paramE Exception class (this must be the same as the exception class you are currently catching) */
    @ExceptionHandler(Exception.class) It is also possible to capture only one class
    public void errorHandler(HttpServletRequest request, HttpServletResponse response,Exception e){
      	/* * You can do everything You want to do * here You get request and Response objects, You can do whatever You want * e.g. * 1. Check the first method * 2 by checking that the request received an Accept from the initial message. If you also want to return a page, use the Response object to redirect to your own error page. You even get the exception object */
      
        String accept = request.getHeader("Accept");
				// What response should be made based on the string
      
        try {
            response.setStatus(500);
            response.getWriter().write("hello");
        } catch(IOException ex) { ex.printStackTrace(); }}}Copy the code

3. Comparison of the two methods:

  • The first method is to place pages with error status codes in the current project for SpringBoot to find. Custom error messages returned are also supported
  • The second method is to use the idea of AOP directly to handle exception notification with great freedom.
  • I personally recommend using the second method because of the high degree of freedom, the ability to change at any time according to your own business logic, and also the great usefulness. The next article will provide a good example
  • After the second method is used, all error pages and custom error messages placed in the first method are invalid