为什么web项目需要统一拦截处理异常。
首先,web项目中进行异常的拦截和统一处理是常规操作和常见操作,但不是非做不可的必选动作。
那为什么是开发中经常这样操作呢?个人理解原因有二:
(1)对前端用户友好。当后端代码执行产生异常之后,不会将后端复杂的异常信息返回给前端,而是经过处理后的简化的友好的信息。
(2)对后端开发友好。将所有异常处理放在一个类中进行集中的统一处理,只需要修改一处,即可拦截所有的异常,降低了开发的复杂度。
一、自定义异常类
在web项目中,“自定义异常类”经常与“异常拦截处理”搭配使用。在出现诸如“前端提交字段不符合要求”、“结果查询为空”
、“用户名重复”等影响代码继续执行的情况时,可以直接抛出自定义异常,由统一拦截器捕获后,将信息返回给前端。
package com.example.demo.exception;
import lombok.Data;
@Data
public class MyException extends RuntimeException {
private String msg;
private int code = 500;
public MyException(String msg) {
super(msg);
this.msg = msg;
}
public MyException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public MyException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public MyException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}
二、统一拦截处理异常
异常的统一拦截与处理主要通过@RestControllerAdvice
和@ExceptionHandler()
注解来实现。
@RestControllerAdvice
是@Controller注解加强类,相当于@ControllerAdviser
+ @ResponseBody
。@ControllerAdviser
注解作用对象为TYPE,包括类、接口和枚举等,在运行时有效,并且可以通过Spring扫描为bean组件。其可以包含由@ExceptionHandler、@InitBinder 和@ModelAttribute标注的方法,可以处理多个Controller类,这样所有控制器的异常可以在一个地方进行处理。
//@RestControllerAdvice注解源码
@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 { };
}
Spring 3.0 引入的@ExceptionHandler
注解可以让我们在一个handler方法或者类中统一处理controller抛出的异常,使得写出的代码清晰。当一个Controller中有方法加了@ExceptionHandler
之后,这个Controller其他方法中没有捕获的异常就会以参数的形式传入加了@ExceptionHandler
注解的那个方法中。当@RestControllerAdvice
和@ExceptionHandler()
注解组合之后,所有controller层面的异常都会统一集中处理。如下定义的ExceptionAdvice类,就是统一处理异常的类。
package com.example.demo.config;
import com.example.emos.wx.exception.EmosException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** * 捕获全局异常,并做处理 * @author wanglong * @ControllerAdvice 和 @RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。 * RestControllerAdvice = ControllerAdvice + ResponseBody */
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public String exceptionHandler(Exception e){
log.error("执行异常",e);
if(e instanceof MethodArgumentNotValidException){
MethodArgumentNotValidException exception= (MethodArgumentNotValidException) e;
//返回精简消息(具体某个字段没有通过校验的原因)
return exception.getBindingResult().getFieldError().getDefaultMessage();
}
else if(e instanceof MyException){
MyException exception= (MyException) e;
return exception.getMsg();
}
else if(e instanceof UnauthorizedException){
return "你不具备相关权限";
}
else{
return "后端执行异常";
}
}
}