Source code repository: github.com/zhshuixian/…

In the previous section “Shiro (Token) Login and Registration “, we mainly introduced the Spring Boot integration Shiro to implement Token login and authentication. In this section, we will implement Spring Boot global exception handling. Return the exception to the front end as JSON wrapped in a uniform style.

When I was developing React + Spring Boot, THE React debug crashed because there was no unified exception handling. React (not familiar with React), it was later discovered that the format and status code returned by the exception was the problem.

For Spring Boot applications, global exception processing is necessary. Exceptions are returned to the front end in uniform JSON style, which facilitates the front end to handle API interface exceptions and track problems. However, Spring Boot’s own error exception handling may not be consistent with our actual development.

For example, in the previous 06-security-token project, we returned json with the following style after successful login:

{
    "status": "SUCCESS"."message": "Token returned"
}
Copy the code

If we have an incorrect username or password, we will return json like this:

{
    "timestamp": "The 2020-04-15 T16: for 414 + 0000"."status": 403."error": "Forbidden"."message": "Access Denied"."path": "/api/user/login"
}
Copy the code

Obviously, it is very inconvenient for our front end to call API return processing. If all @RestController add try-catch to catch and handle exceptions, such as parameter check exceptions, password errors and other exceptions in login services, the code will be bloated and more time will be spent.

    // *06-security-token* org.xian.token.service.SysUserService
    public MyResponse login(final SysUser sysUser) {
        try {
            // Verify that the username and password are correct
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(sysUser.getUsername(),
                            sysUser.getPassword()));
        } catch (BadCredentialsException e) {
            return new MyResponse("ERROR"."Incorrect username or password");
        }
        // Generate tokens and query user permissions
        SysUser sysUserData = sysUserMapper.selectByUsername(sysUser.getUsername());
        return new MyResponse("SUCCESS",
                tokenUtils.createToken(sysUserData));
    }
Copy the code

1. Spring Boot global exception processing

A new project, 09-error-Controller, only needs to introduce web dependencies.

Create a class associated with the project

Create MyResponse as an entity class for uniform style JSON

public class MyResponse{
    private String status;
    private String message;
    // omit getter setter constructor
}
Copy the code

Create a new User and pass in the validation of the parameters

public class User {
    @notempty (message = "username cannot be empty ")
    @ the Pattern (regexp = "^ [a zA - Z0-9] 3 dec} {$", message =" username should be 3 to 16 English, digital ")
    private String username;
    // omit getter setter constructor
}
Copy the code

Create MyException and customize the exception

public class MyException  extends Throwable{
    private final String status;
    private final String  message;

    public MyException(String status,String message) {
        this.status=status;
       this.message=message;
    }

    public String getStatus(a) {
        return status;
    }

    @Override
    public String getMessage(a) {
        returnmessage; }}Copy the code

New ErrorController

@RestController
public class ErrorController {

    @RequestMapping("/throw")
    public String myThrow(a) throws MyException {
        // Throw MyException
        throw new MyException("Error"."Throw MyException");
    }

    @PostMapping("/user")
    public String user(@RequestBody @Valid final User user) {
        // Check the parameters
        return "Parameter test passed"; }}Copy the code

Run the project to access the non-existent path and the above path respectively: for example, using the Get method to access /user returns the following:

{
    "timestamp": "The 2020-04-16 T16:43:41. 123 + 0000"."status": 405."error": "Method Not Allowed"."message": "Request method 'GET' not supported"."path": "/user"
}
Copy the code

Add global exception handling, @restControllerAdvice

New ErrorRestControllerAdvice

@RestControllerAdvice
public class ErrorRestControllerAdvice {
    /** Global exception capture processing returns 401 status */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public MyResponse errorHandler(Exception ex) {
        return new MyResponse("ERROR", ex.getMessage());
    }

    /** Custom exception capture, return 500 status */
    @ExceptionHandler(value = MyException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public MyResponse myException(MyException e) {
        return new MyResponse(e.getStatus(), e.getMessage());
    }

    /** Http Method returns 405 */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public MyResponse httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return new MyResponse("ERROR", e.getMessage());
    }

    /** 404 NOT_FOUND exception */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public MyResponse noHandlerFoundException(NoHandlerFoundException e) {
        return new MyResponse("ERROR"."Resources do not exist");
    }

    /** Return this error notification if the RequestBody is empty, returning 400 bad Request */
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public MyResponse httpMessageNotReadableException(a) {
        return new MyResponse("ERROR"."Please pass in parameters");
    }

    /** RequestBody returns 400 Bad Request */ if a mandatory parameter is null
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public MyResponse methodDtoNotValidException(Exception ex) {
        MethodArgumentNotValidException c = (MethodArgumentNotValidException) ex;
        List<ObjectError> errors = c.getBindingResult().getAllErrors();
        StringBuffer errorMsg = new StringBuffer();
        errors.forEach(x -> errorMsg.append(x.getDefaultMessage()).append(""));
        return new MyResponse("ERROR", errorMsg.toString()); }}Copy the code

Code parsing:

@ RestControllerAdvice: Equal to @responseBody + @ControllerAdvice, @ControllerAdvice is the Spring enhanced Controller Controller that defines @ExceptionHandler, @initBinder and @ModelAttribute methods

@ExceptionHandler(value = exception.class) : a method that executes the Exception caught by value with its annotations. Annotations in @Controller are only valid in the current class, while annotations in @ControllerAdvice are valid globally.

@responseStatus (httpstatus.bad_request) : Sets the status return code for this exception.

Rerun the project, distribute access to nonexistent paths, and observe the difference between /throw, /user. For example, access to /user via GET would return the following:

{
    "status": "ERROR"."message": "Request method 'GET' not supported"
}
Copy the code

Spring Boot’S wechat login is temporarily cancelled because individual developers cannot apply for it. The next project is Spring Boot’s Redis cache integration.