SpringBoot统一异常处理(4)
在后端发生异常或者是请求出错时,前端通常显示如下
对于用户来说非常不友好。本文主要讲解如何在SpringBoot应用中使用统一异常处理。
实现方式
第一种:使用@ControllerAdvice和@ExceptionHandler注解
第二种: 使用ErrorController类来实现。
第一种:使用@ControllerAdvice和@ExceptionHandler注解
package com.hanzhenya.learnspringboot.advice; import com.hanzhenya.learnspringboot.util.ResultInfo; import org.springframework.http.HttpStatus; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @Description: 切面异常处理 * @author: 韩振亚 * @date: 2021年03月29日 13:38 */ @RestControllerAdvice public class GlobalControllerAdvice{ private static final String BAD_REQUEST_MSG = "客户端请求参数错误"; // <1> 处理 form data方式调用接口校验失败抛出的异常 @ExceptionHandler(BindException.class) public ResultInfo bindExceptionHandler(BindException e) { List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); List collect = fieldErrors.stream().map(o ->o.getDefaultMessage()).collect(Collectors.toList()); return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect); } // <2> 处理 json 请求体调用接口校验失败抛出的异常 @ExceptionHandler(MethodArgumentNotValidException.class) public ResultInfo methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors(); List collect = fieldErrors.stream().map(o ->o.getDefaultMessage()).collect(Collectors.toList()); return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect); } // <3> 处理单个参数校验失败抛出的异常 @ExceptionHandler(ConstraintViolationException.class) public ResultInfo constraintViolationExceptionHandler(ConstraintViolationException e) { Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations(); List collect = constraintViolations.stream().map(o -> o.getMessage()).collect(Collectors.toList()); return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect); } }
注解@ControllerAdvice表示这是一个控制器增强类,当控制器发生异常且符合类中定义的拦截异常类,将会被拦截。
可以定义拦截的控制器所在的包路径
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] assignableTypes() default {}; Class<? extends Annotation>[] annotations() default {}; }
注解ExceptionHandler定义拦截的异常类
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExceptionHandler { Class<? extends Throwable>[] value() default {}; }
第二种: 使用ErrorController类来实现。
系统默认的错误处理类为BasicErrorController,将会显示如上的错误页面。
这里编写一个自己的错误处理类,上面默认的处理类将不会起作用。
getErrorPath()返回的路径服务器将会重定向到该路径对应的处理类,本例中为error方法。
package com.hanzhenya.learnspringboot.advice; import com.hanzhenya.learnspringboot.util.ResultInfo; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.jws.WebResult; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @Description: TODO * @author: 韩振亚 * @date: 2021年03月31日 14:38 */ @Slf4j @RestController public class HttpErrorController implements ErrorController { private final static String ERROR_PATH = "/error"; @ResponseBody @RequestMapping(path = ERROR_PATH ) public ResultInfo error(HttpServletRequest request, HttpServletResponse response){ log.info("访问/error" + " 错误代码:" + response.getStatus()); return new ResultInfo().success(response.getStatus(),"请求参数错误",""); } @Override public String getErrorPath() { return ERROR_PATH; } }
区别
1.注解@ControllerAdvice方式只能处理控制器抛出的异常。此时请求已经进入控制器中。
2.类ErrorController方式可以处理所有的异常,包括未进入控制器的错误,比如404,401等错误
3.如果应用中两者共同存在,则@ControllerAdvice方式处理控制器抛出的异常,类ErrorController方式处理未进入控制器的异常。
4.@ControllerAdvice方式可以定义多个拦截方法,拦截不同的异常类,并且可以获取抛出的异常信息,自由度更大。