SpringBoot + Vue front-end and back-end separate development: global exception handling and unified result encapsulation

SpringBoot + Vue front-end and back-end separate development: global exception handling and unified result encapsulation

Exception handling in front-end and back-end separation development

  In our front-end and back-end separation projects, sometimes it is unavoidable that the back-end service reports an error and throws an exception. If the global exception handling mechanism is not configured, the 5XX page of tomcat or nginx will be returned by default. For ordinary users, it is not necessary Too friendly, users don't understand what's going on. At this time, our programmers need to design and return a friendly and simple format to the front-end, and then the front-end returns an error message that can be understood by the user, instead of throwing a java exception to the user.
  The backend returns an error message to the frontend, and the frontend needs to be able to receive this error message and notify the user, that is, the frontend also needs to handle exceptions. In the Vue project, the frontend uses axios to send requests to the backend, and the frontend needs to configure global axios Interceptor, which intercepts the http response returned by the backend to determine whether the request is successful or not, and returns
  the corresponding error message if it fails. A unified standard, the data returned by the agreed result is normal or encounters an exception. Otherwise, the data returned by the backend is varied, and the frontend cannot judge whether the request is successful or not.

Unified result encapsulation

  The unified result packaging is determined by the front-end and back-end negotiations, and the packaging types may be different for different projects.
  Here we use a simple unified result encapsulation class to demonstrate this important link of front-end and back-end interaction. Generally speaking, several elements are necessary in the unified result encapsulation class:

  • Whether the request is successful can be indicated by code (for example, 200 means success, 400 means exception)
  • Request result message message
  • Request result data data

  Here we use a Result class to represent the unified result encapsulation class, so the Result class must have the following three attributes:
insert image description here
  What methods should there be in Result? There are only two cases of success and failure in the request. Static methods should be defined in Result to generate a Result object and return it to the front end. The parameters are the three attributes of the Result object. So we define the following two methods
insert image description here
  : Not enough, because these two methods are not particularly convenient to use. For most successful requests, the code is 200. The successful message is not important, and the data is what the front end needs. That is to say, in most successful requests, we only need to pass the data to the front end. For the method of creating Result, there is only one parameter, that is data, the code is directly 200, and the message is that the operation is successful. So we define a succ overload method, which allows us to use the result package more conveniently: the
insert image description here
  same is true for most request failures. Generally, when a request fails, the backend will not return data to the frontend. When the request fails, it will be abnormal. The error message message is what the front end needs, and the code can also be directly defined as 400, because 400 represents a bad request in http, that is, the request fails. So we define an overload method of fail:
insert image description here
  at this point, we have defined the unified result encapsulation class, and its complete code is as follows:

@Data
public class Result implements Serializable {
    
    

    private int code;
    private String msg;
    private Object data;

    public static Result succ(Object data) {
    
    
        return succ(200, "操作成功", data);
    }

    public static Result fail(String msg) {
    
    
        return fail(400, msg, null);
    }

    public static Result succ (int code, String msg, Object data) {
    
    
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    public static Result fail (int code, String msg, Object data) {
    
    
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
}

  It should be noted that the Result class implements the Serializable interface, which is for serialization, can be converted into JSON, etc. for network transmission, and then transmitted to the front end

Backend global exception capture and processing

  We can define a class called GlobalExceptionHandler to capture and handle global exceptions. This class needs to be annotated with @RestControllerAdvice
  @RestControllerAdvice, which can be used to define @ExceptionHandler, @InitBinder, @ModelAttribute, and applied to all @RequestMapping. @RestControllerAdvice is a component annotation that enables its implementation class to be automatically discovered by classpath scanning. The @RestControllerAdvice annotation is mainly used in conjunction with @ExceptionHandler to handle exceptions uniformly.

  That is, the @RestControllerAdvice annotation defines global controller exception handling

  We can define multiple overloaded handler methods in this class to capture and handle exceptions. The method parameters of the handler are various exceptions. The handler method needs to be annotated with @ExceptionHandler. The element value of the annotation can specify the captured What kind of exception is it, such as @ExceptionHandler(value = RuntimeException.class)catching a runtime exception. The handling of this exception marked with the @ExceptionHandler annotation is global, and all exceptions of the same type as the value value will go to this place for processing.

  The handler method also needs to use the @ResponseStatus annotation, which can be used in conjunction with the @ResponseStatus annotation and a custom exception to return a custom response status code and a custom error message to the client. That is, the @ResponseStatus annotation is used in conjunction with the @ExceptionHandler annotation. Here we mainly need to use it to set the http status codes corresponding to different exceptions. There are two parameters in the @ResponseStatus annotation, the value attribute sets the status code of the exception, and easeon is the description of the exception.

  The complete code example of the GlobalExceptionHandler global exception handling class is as follows:

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    public Result handler(RuntimeException e) {
    
    
        log.error("运行时异常:----------------{}", e.getMessage());
        return Result.fail(e.getMessage());
    }

    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(value = AccessDeniedException.class)
    public Result handler(AccessDeniedException e) {
    
    
        log.info("security权限不足:----------------{}", e.getMessage());
        return Result.fail("权限不足");
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handler(MethodArgumentNotValidException e) {
    
    
        log.info("实体校验异常:----------------{}", e.getMessage());
        BindingResult bindingResult = e.getBindingResult();
        ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();
        return Result.fail(objectError.getDefaultMessage());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public Result handler(IllegalArgumentException e) {
    
    
        log.error("Assert异常:----------------{}", e.getMessage());
        return Result.fail(e.getMessage());
    }


}

  Since there are many different exceptions in the project, we can also customize the exception class and add the capture of custom exceptions in GlobalExceptionHandler. For example, we can customize a login verification code exception:

public class CaptchaException extends AuthenticationException {
    
    

    public CaptchaException(String msg) {
    
    
        super(msg);
    }
}

  Then add the custom exception handling to the global exception handling:

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = CaptchaException.class)
    public Result handler(CaptchaException e) {
    
    
        log.error("登录验证码异常:----------------{}", e.getMessage());
        return Result.fail(e.getMessage());
    }

  Note that the exception Exception in Java is a subclass of Throwable, and the getMessage() method is defined in Throwable to return the specific information of the exception:
insert image description here
  MethodArgumentNotValidException in the example inherits BindException, which is defined in the spring entity validation. The exception has a BindingResult attribute. BindingResult is used in entity class verification information to return result binding. The BindingResult class inherits the Errors class in spring validation. The Errors class is used to store and expose information about data binding and validation errors for specific objects . bindingResult.hasErrors() can determine whether the verification is passed. The Errors class has a getAllErrors() method, which is used to obtain all errors in the global and Field fields. The ObjectError class is used to encapsulate object error

Front-end axios post-interceptor

  Create the axios.js file in the src directory of Vue to configure axios. The front end uses the axios post-interceptor to intercept the http response and obtain the information returned by the back end.

  The complete code example of axios.js is as follows:

import axios from "axios";
import router from "./router";
import Element from "element-ui"

axios.defaults.baseURL = "http://localhost:8082"

const request = axios.create({
    
    
    timeout: 5000,
    headers: {
    
    
        'Content-Type': "application/json; charset=utf-8"
    }
})

// 后置拦截
request.interceptors.response.use(response => {
    
    
        console.log("response ->" + response)
        // 这里是response的拦截
        let res = response.data
        if (res.code === 200) {
    
    
            return response
        } else {
    
    
            Element.Message.error(!res.msg ? '系统异常' : res.msg)
            // 拒绝流程继续往下走
            return Promise.reject(response.data.msg)
        }
    },
    // 如果业务中出现异常
    error => {
    
    
        // 先看后端返没返报错信息,返了就用后端的
        if (error.response.data) {
    
    
            error.message = error.response.data.msg
        }

        // 401没权限
        if (error.response.status === 401) {
    
    
            router.push("/login")
        }

        Element.Message.error(error.message, {
    
    duration:3000})

        return Promise.reject(error)

    }
)

export default request

response => {...}There are and   in the post-interceptor error => {...}, which respectively represent the processing of response data and error requests. For response data, if its code is 200, it means that the request is successful, and response will be returned. Otherwise, enter exception handling, here use element-UI to pop up an error message, if the msg of the response data is empty, the error message is a system exception, otherwise the error message is the exception message in the response data, call Promise.reject to reject the follow-up process. The handling of bad requests is similar

Guess you like

Origin blog.csdn.net/qq_44709990/article/details/123048867
Recommended