Spring boot - @ controlleradvice intercepts exceptions and handles them uniformly

Keywords: Java Spring SpringBoot xml JSON

SpringBoot is the product of a series of problems to simplify the creation, operation, debugging and deployment of Spring applications,

The feature of automatic assembly enables us to pay more attention to the business itself rather than the external XML configuration. We can easily build a WEB project by following the specification and introducing the relevant dependency

 

In the actual project development, there are often various exceptions in the program, especially for us as the server developers,

Always write the interface to the front-end to call. In the case of division and cooperation, exceptions cannot be avoided. If the wrong information is directly exposed to the user,

This kind of experience can be imagined, and for hackers, detailed exception information often provides a great help

First glance abnormality

A simple exception request interface

@GetMapping("/test1")
public String test1() {
    // This is just a simulation exception. Suppose there is an error in business processing, or a null pointer, etc...
    int i = 10 / 0;
    return "test1";
}

Found when opening browser to access it

Or use simulation tools such as postman

If this interface is used by a third party or by your own company's system, see that this kind of error estimation is rampant .

Stupid method (extremely not recommended)

Try catch is used to manually capture exception information, and then return the corresponding Result set. It is believed that many people have seen similar code (such as: encapsulated as Result object);

Although this method indirectly solves the problem of error exposure, the same disadvantages are obvious, increasing a large amount of code, when there are too many exceptions, the corresponding catch layer is more and more,

It is difficult to manage the match between these business exceptions and error codes, so the best way is to control the whole situation through simple configuration .

@GetMapping("/test2")
public Map<String, String> test2() {
    Map<String, String> result = new HashMap<>(16);
    // TODO Capture all code blocks directly, and then cache
    try {
        int i = 10 / 0;
        result.put("code", "200");
        result.put("data", "Specific returned result set");
    } catch (Exception e) {
        result.put("code", "500");
        result.put("message", "Request error");
    }
    return result;
}

Specific code

Through the above reading, you can roughly understand why it is necessary to catch exceptions globally. Next, let's look at the solutions provided by Spring Boot

Import dependency

Add spring boot starter web dependency to pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Custom exception

In the application development process, in addition to the system's own exceptions, the exceptions used in different business scenarios are also different. In order to easily handle the global exceptions with the title, define your own exceptions and see how to capture them

/**
 * Custom exception
 */
public class CustomException extends RuntimeException {
    private static final long serialVersionUID = 4564124491192825748L;

    private int code;

    public CustomException() {
        super();
    }
    public CustomException(int code, String message) {
        super(message);
        this.setCode(code);
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
}

Exception information template

Define the format of returned exception information, so that the style of exception information is more uniform

/**
 * Exception information template
 */
public class ErrorResponseEntity {

    private int code;
    private String message;

    public ErrorResponseEntity(int code, String message) {
        this.code = code;
        this.message = message;
    }
    // ellipsis get/set
}

Control layer

Take a closer look at whether the normal code written in peace is the same. Don't worry. Then take a look .

import com.winterchen.exception.CustomException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ExceptionController {

    @GetMapping("/test3")
    public String test3(Integer num) {
        // TODO It is necessary to demonstrate whether the parameter is empty to pass @RequestParam(required = true)  You can control it
        if (num == null) {
            throw new CustomException(400, "num Can not be empty");
        }
        int i = 10 / num;
        return "result:" + i;
    }
}

Exception handling (critical)

Notes overview

  • @ControllerAdvice catches the < wiz ﹣ TMP ﹣ highlight ﹣ tag class = "cm searching" > exception thrown by the Controller layer. If @ ResponseBody return information is added, it is in JSON format.
  • @Restcontrolleradvise is the combination of @ controlleradvise and @ ResponseBody.
  • @The ExceptionHandler uniformly handles a kind of < wiz ﹣ TMP ﹣ highlight ﹣ tag class = "cm searching" > exception, reducing code repetition rate and complexity.

Create a GlobalExceptionHandler class and add the @ RestControllerAdvice annotation to define the exception notification class. Then add @ ExceptionHandler to the defined method to catch the exception

import com.winterchen.exception.CustomException;
import com.winterchen.exception.ErrorResponseEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Global exception handling
 */
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    /**
     * You can define more than one @ ExceptionHandler({}) to catch
     * @param request  request
     * @param e        exception
     * @param response response
     * @return Response results
     */
    @ExceptionHandler(CustomException.class)
    public ErrorResponseEntity customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        CustomException exception = (CustomException) e;
        return new ErrorResponseEntity(exception.getCode(), exception.getMessage());
    }

    /**
     * Catch RuntimeException exception
     * If you think it is too troublesome to pass if (e instance of xxxexception) in an exception handler
     * Then you can write several different exceptionhandlers to handle different exceptions
     * @param request  request
     * @param e        exception
     * @param response response
     * @return Response results
     */
    @ExceptionHandler(RuntimeException.class)
    public ErrorResponseEntity runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        RuntimeException exception = (RuntimeException) e;
        return new ErrorResponseEntity(400, exception.getMessage());
    }

    /**
     * General interface mapping exception handler
     */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (ex instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
            return new ResponseEntity<>(new ErrorResponseEntity(status.value(), exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status);
        }
        if (ex instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
            logger.error("Parameter conversion failed, method:" + exception.getParameter().getMethod().getName() + ",Parameters:" + exception.getName()
                    + ",Information:" + exception.getLocalizedMessage());
            return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "Parameter conversion failed"), status);
        }
        return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "Parameter conversion failed"), status);
    }
}

Main function

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootExceptionApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootExceptionApplication.class, args);
    }
}

test

After completing the preparation, start Chapter17Application. Through the following test results, it can be found that it's really so easy, the code has become clean, and the extensibility has become better

visit http://localhost:8080/test3

{"code":400,"message":"num Can not be empty"}

visit http://localhost:8080/test3?num=0

{"code":400,"message":"/ by zero"}

visit http://localhost:8080/test3?num=5

result:2

 

Reprint link: http://blog.battcn.com/2018/06/01/springboot/v2-other-exception/

Posted by lssjg on Sun, 22 Dec 2019 03:49:47 -0800