SpringBoot error handling mechanism and custom exception handling

In the last article, we explained the use of Hibernate Validation to verify data. After verifying the data, if an error occurs, we need to return an error message to the customer. Therefore, in this section, we will explain the default error handling mechanism of SpringBoot and how to customize it. exception to handle request errors.

1. SpringBoot's default error handling mechanism

When we send a request, what will SpringBoot do if 404 occurs? Let's send a non-existent request to verify and see the page result. As follows:
write picture description here
When an error occurs inside the server, what will the page return?

    @GetMapping("/user/{id:\\d+}")
    public User get(@PathVariable String id) {
        throw new RuntimeException();
    }

write picture description here

We will find that no matter what error occurs, SpringBoot will return a status code and an error page. How does this error page come from?
Let's take a look at the source code of the SpringBoot error handling module. It will be very clear. By default, an error occurs. It will forward the request to the BasicErrorController controller to process the request. The following is the source code of the controller class:

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

    private final ErrorProperties errorProperties;

    /**
     * Create a new {@link BasicErrorController} instance.
     * @param errorAttributes the error attributes
     * @param errorProperties configuration properties
     */
    public BasicErrorController(ErrorAttributes errorAttributes,
            ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties,
                Collections.<ErrorViewResolver>emptyList());
    }

    /**
     * Create a new {@link BasicErrorController} instance.
     * @param errorAttributes the error attributes
     * @param errorProperties configuration properties
     * @param errorViewResolvers error view resolvers
     */
    public BasicErrorController(ErrorAttributes errorAttributes,
            ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }

    @Override
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }

    @RequestMapping(produces = "text/html")
    public ModelAndView errorHtml(HttpServletRequest request,
            HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    }

    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }

From the source code above, we can see that it has two RequestMapping methods to map bad requests. Why are there two? In fact, the errorHtml method maps the request sent by the browser, and the error method maps not the browser but the error request sent by other software app clients.
After reading the above source code, can we define the 404 or 500 error page to return to the client? Of course, we can create a new folder resources/error under the src/main/resources path, then create 404.html and 500.html, and then write our own error content:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
    亲,您所访问的页面不存在
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>500</title>
</head>
<body>
    服务器内部错误
</body>
</html>

However, it should be noted that the above method of customizing the page is only valid on the browser side, not the request sent by the browser will not take effect. So let's talk about how to customize exception handling to solve this problem.

2. Custom exception handling

How to customize the exception handling error message sent by the client? If we query a user, the user does not exist, can we return the id of the user that does not exist to the customer? Isn't this effect a better experience for customers? Let's implement this function below.
First we need to write an exception class that inherits the RuntimeException class:

package cn.shinelon.exception;

/**
 * @author Shinelon
 *
 */
public class UserNotExistException extends RuntimeException{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String id;
    public UserNotExistException(String id) {
        super("user not exist");
        this.id=id;
    }
public void setId(String id) {
    this.id = id;
}
public String getId() {
    return id;
}
}

Then we need to write a handler class to handle exceptions thrown by the controller layer:

/**
 * 
 */
package cn.shinelon.exception;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * 控制器的异常处理类
 * @author Shinelon
 *
 */
//这个注解是指这个类是处理其他controller抛出的异常
@ControllerAdvice
public class ControllerExceptionHandler {

    //这个注解是指当controller中抛出这个指定的异常类的时候,都会转到这个方法中来处理异常
    @ExceptionHandler(UserNotExistException.class)
    //将返回的值转成json格式的数据
    @ResponseBody
    //返回的状态码
    @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)     //服务内部错误
    public Map<String,Object> handlerUserNotExistException(UserNotExistException ex){
        Map<String,Object> result=new HashMap<String,Object>();
        result.put("id", ex.getId());
        result.put("message", ex.getMessage());
        return result;
    }
}

This class is annotated with @ControllerAdvice to handle the corresponding exception thrown by the controller layer. Here we handle the UserNotExistException custom exception thrown by the controller, and return the error message and user id to the client in the format of a json string.
Next, we throw this exception in the request method of the controller, and we will see that the exception in the browser is the json data returned by our custom exception.
Controller layer code:

    @GetMapping("/user/{id:\\d+}")
    //@RequestMapping(value="/user/{id:\\d+}",method=RequestMethod.GET)
    @JsonView(User.DetailJsonView.class)
    public User get(@PathVariable String id) {
        throw new UserNotExistException(id);
    }

write picture description here

So far, we have introduced SpringBoot's default error handling mechanism and our custom exception to handle error requests, which is more conducive to our development and brings users better results.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325449879&siteId=291194637