One, foreword

In daily project development, exceptions are common, but how to deal with the exception information more efficiently, so that we can quickly locate the BUG, is very important, not only can improve our development efficiency, but also make your code look more comfortable, SpringBoot projects have certain exception processing, This may not be appropriate for us as developers, so we need to uniformly catch and handle these exceptions.

Without exception handling, when an error occurs, the returned information might look something like the following:

2. Anomaly classification

1. Classification of exceptions from the perspective of definition

(1).

Hardware or operating system errors encountered during the execution of a program. Errors are fatal to a program and cause it to fail to run. Common errors include memory overflow, abnormal running of the JVM itself, and calSS files with no master method. The program cannot handle errors, relying only on external intervention. An Error is an internal system Error that is thrown by the JVM and handed to the system to handle.

(2). Exception

It is an unexpected condition that can be expected during normal operation of the program. For example, database connection breaks, null Pointers, array subscripts out of bounds. Exceptions can lead to abnormal termination of the program, or can be detected in advance, captured and processed, so that the program continues to run. Exceptions are classified by nature into compile exceptions (detectable) and runtime exceptions (undetectable).

A. Exceptions occur during compilation

Also called checkable exceptions, exceptions are usually caused by syntax errors and environmental factors (external resources). For example, I/O exception IOException, database operation SQLException. The feature is that the Java language mandates that all non-runtime exceptions be caught and handled. Enforce program robustness and security through a code of conduct.

B. Runtime exceptions

Also called unchecked exception RuntimeException, these exceptions are usually caused by program logic errors, i.e., semantic errors. Such as arithmetic exception, null pointer exception NullPointerException, subscript IndexOutOfBoundsException seams. Runtime exceptions should be exposed during program testing and debugged by the programmer rather than caught.

2. Classify anomalies from a development perspective

We can also classify anomalies as known and unknown.

(1). Known anomalies

Represents exceptions that we can control, such as when a resource was not found, to return to the front end, throw the resource was not found. If a parameter is missing while validating it, throw a parameter missing to the front end.

(2). Unknown exceptions

It means that we do not know when the program may report an error, may be not strict judgment somewhere, resulting in null pointer or subscript out of bounds.

3. How to gracefully catch exceptions (with emphasis on known exception handling)

We know that with SpringBoot, we can use @ControllerAdvice and @ExceptionHandler for global exception catching, but how can we make exception catching more elegant?

We can customize exception handling from known and unknown exceptions.

1. Handle unknown exceptions

  1. Create a newGlobalExceptionAdviceGlobal exception handling class, plus@ControllerAdviceannotations
  2. Catch the Exception class
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public ReturnMsgEntity handlerException(HttpServletRequest request, Exception e) {
        String uri = request.getRequestURI();
        String method = request.getMethod();
        ReturnMsgEntity message = new ReturnMsgEntity(9999, codeConfiguration.getMessage(9999), null, method + "" + uri);
        return message;
    }
Copy the code

To prevent hard coding, a codeConfiguration configuration class is written to read files from a path and encapsulate them into a map

@Component
@ConfigurationProperties(prefix="aisino")
@PropertySource("classpath:config/exception-code.properties")
public class ExceptionCodeConfiguration {
    private Map<Integer,String> codes = new HashMap<>();

    public  String getMessage(int code){
        return codes.get(code);
    }

    public Map<Integer, String> getCodes(a) {
        return codes;
    }

    public void setCodes(Map<Integer, String> codes) {
        this.codes = codes; }}Copy the code

The exception-code.properties configuration file is as follows:

Aisino.codes [9999] = server unknown exception aisino.codes[10000] = Universal exception aisino.codes[10001] = universal parameter error aisino.codes[0] = OK Aisino. Codes [10002] = Resources not foundCopy the code
  1. The above unknown exception handling is done, see the effect, simulate a null-pointer exception interface, access results are as follows

2. Handling of known exceptions (key points)

There are many known exceptions, such as forbidden access, missing parameters, no resource found, no authentication, do we want to write every error in the GlobalExceptionAdvice class? Isn’t it hard to maintain, like how to throw a resource when it doesn’t exist?

To solve this problem, you can catch a self-defined HttpException in GlobalExceptionAdvice. All other error classes are HttpException, which in turn inherits from RuntimeException. The HttpException class throws its own error as follows:

public class HttpException extends RuntimeException{
    // Custom error code
    protected Integer code;
    // The default status code
    protected Integer httpStatusCode = 500;

    public Integer getCode(a) {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Integer getHttpStatusCode(a) {
        return httpStatusCode;
    }

    public void setHttpStatusCode(Integer httpStatusCode) {
        this.httpStatusCode = httpStatusCode; }}Copy the code

We also need to define other concrete subclasses that inherit from the above class, such as NotFoundException, as follows:

public class NotFoundException extends HttpException {
    public NotFoundException(int code){
        this.httpStatusCode = 404;
        this.code = code; }}Copy the code

The HttpException class can be captured in GlobalExceptionAdvice as follows:

  @ExceptionHandler(HttpException.class)
    @ResponseBody
    public ResponseEntity<ReturnMsgEntity> handlerHttpException(HttpServletRequest request, HttpException e) {
        // Get the request path
        String requestUrl = request.getRequestURI();
        // Get the request method
        String method = request.getMethod();
        LLDB EtCode Obtains specific error information according to the status code
        ReturnMsgEntity message = new ReturnMsgEntity(e.getCode(), codeConfiguration.getMessage(e.getCode()), null, method + "" + requestUrl);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // Set our custom HTTP status code, for example, 404 is not found, the front-end received status code is not 200, is 404
        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
        ResponseEntity<ReturnMsgEntity> responseEntity = new ResponseEntity<>(message, headers, httpStatus);
        return responseEntity;

    }
Copy the code

Finally, let’s use:

If no user information is displayed, the following information is displayed:

From this we can see that the status code is 404 defined by ourselves and the code returned is passed in by us.


This is the end of this article. The next article will cover parameter validation for unified exception handling. Thank you for reading.