SpringBoot global exception handling and custom 404 page

1. Analysis of error handling principle

In a web project created using Spring Boot, when the page we requested does not exist (http status code is 404), or an exception occurs (http status code is generally 500), Spring Boot will return an error message to us.

In other words, in the SpringBoot web project, an error interface of / error will be automatically created to return the error information. But for different access methods, there will be the following two different return information. This mainly depends on Acceptthe value of the http header information when you visit to specify what types you can receive

  • Header information when accessing with a browser and its return result
Accept: text/html

  • Use other devices, such as mobile client, etc. to access the header information and its returned results (generally in a separate front-end architecture)
Accept: */*

Second, error handling

There are two main ways to handle exceptions:

1. Use SpringBoot's automatic configuration principle for exception handling

SpringBoot automatically configures a class ErrorMvcAutoConfigurationto handle exceptions, if you are interested, you can take a look, and then define an error BasicErrorController class in this class, the main code is as follows:

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

  	/**
  	 * 错误的页面响应 
  	 */
    @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());
      	// 得到一个modelAndView对象
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }
		
  /**
   * 错误的json响应
   */
    @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 new ResponseEntity(body, status);
        }
    }
}

More code will not be studied in depth, you can take a look if you are interested. The above code means that for different request methods, different results will be returned. The key lies in @RequestMappingthe produces = {"text/html"}attribute of the annotation

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

  • When there is a template engine (can be used to render the page)

The template engine used in the project, such as: thymeleaf, freemarker, etc., is used for page rendering. Create / error folder in templates and add the .html file corresponding to the error status code, as shown below:

Here 404 and 500 are the determined error status codes, and 4xx indicates other errors starting with 4, such as 400, 401, etc. Of course, you can set a corresponding error page for each status code, but there is no benefit in doing so, so use a general term like 4xx.html instead.

The following information (that is, the content in the ModelAndView object) can be obtained on our error page:

Field name Explanation
timstamp Timestamp
status Error status code
error Error message
exception Exception object
message Exception message
path Page path

Attentive friends will find that this is actually the json content returned when you use the mobile phone to request

For example: add the above information in the code, and then write an error code in the back end:

@RequestMapping("haserror")
@ResponseBody
public Object myError(){
  int i =10/0;
  return "something is error";
}
这是一个错误页面:
<ul>
    <li>错误状态码:[[${status}]]</li>
    <li>错误消息:[[${error}]]</li>
    <li>异常对象:[[${exception}]]</li>
    <li>异常消息:[[${message}]]</li>
    <li>当前时间:[[${timestamp}]]</li>
</ul>

  • Without template engine

When the template engine is not used in the project, just move the entire error folder to the static folder.

However, at this time, the above information cannot be obtained, because this is a static resource, and there is no template engine for rendering.

2), return the corresponding json string

There is nothing to say about this, it returns a json string. The format is as follows:

{
"timestamp": "2020-04-22T16:13:37.506+0000",
"status": 500,
"error": "Internal Server Error",
"message": "/ by zero",
"path": "/hello/haserror",
"reason": "完了,你写的代码又产生了一次线上事故"
}

3), custom page returns information

This is the most important content, because this information is not only returned as json, but also can be obtained in the error page above, or it can directly return a json. In fact, it is also very simple, just add an ErrorAttributesobject in the Spring container , here I choose to inherit a subclass of it.

@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        //调用父类的方法,会自动获取内置的那些属性,如果你不想要,可以不调用这个
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);

        //添加自定义的属性
        errorAttributes.put("reason","完了,你写的代码又产生了一次线上事故");
        // 你可以看一下这个方法的参数webRequest这个对象,我相信你肯定能发现好东西

        return errorAttributes;
    }
}

That ’s all. Test the availability of one of our custom properties with two request methods:

2. Use AOP exception notification for processing (recommended)

Its principle is to obtain a global exception notification and then handle it. We only need to write the side code in the project (in fact, the above is just a class with custom exception information)

@ControllerAdvice
public class ErrroAcvice {

    /**
     * 全局捕获异常的切面类
     * @param request 请求对象,可不传
     * @param response 响应对象,可不传
     * @param e 异常类(这个要和你当前捕获的异常类是同一个)
     */
    @ExceptionHandler(Exception.class) //也可以只对一个类进行捕获
    public void errorHandler(HttpServletRequest request, HttpServletResponse response,Exception e){
      	/*
      	 * You can do everything you want to do
         * 这里你拿到了request和response对象,你可以做任何你想做的事
         * 比如:
         *	1.用request从头信息中拿到Accept来判断是请求方可接收的类型从而进行第一个方法的判断
         *	2.如果你也想返回一个页面,使用response对象进行重定向到自己的错误页面就可以了
         *  3.你甚至还拿到了异常对象
      	 */
      
        String accept = request.getHeader("Accept");
				// 根据这个字符串来判断做出什么响应	
      
        try {
            response.setStatus(500);
            response.getWriter().write("hello");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
      
    }
}

3. Comparison of the two methods:

  • The first method is to place some error status code pages in the current project and let Spring Boot find them. Also supports custom error message returned
  • The second method is to directly use the idea of ​​AOP for exception notification processing, which has great freedom.
  • I personally recommend the second method, because it has a high degree of freedom, it can be changed at any time according to its own business logic, and it has a great use. There will be a good example in the next article
  • After using the second method, all error pages and custom error messages placed by the first method are invalid

3. Code address:

Pay attention to WeChat public account: Xiaoyu and Java, reply "2000" in the background to get

Guess you like

Origin www.cnblogs.com/Lyn4ever/p/12757947.html