场景
在编写Controller接口时,为避免接口因为未知的异常导致返回不友好的结果和提示。
如果不进行全局异常捕获则需要对每个接口进行try-catch或其他操作。
可以对Controller进行全局的异常捕获和处理,一旦发生异常,则返回通用的500响应码与通用错误提示。
并将异常发生的具体的文件、类、方法、行数信息记录到日志。
@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,
可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理。
需要配合@ExceptionHandler使用。当将异常抛到controller时,可以对异常进行统一处理。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
1、@RestControlAdvice是组合注解,由@ControllerAdvice和@ResponseBody组成。
@ControllerAdvice 提供了多种指定Advice规则的定义方式,默认什么都不写,则是Advice所有Controller,
当然你也可以通过下列的方式指定规则。
通过basePackages指定只对哪些包路径下生效。
也可以通过指定注解来匹配,比如我自定了一个 @CustomAnnotation 注解,
我想匹配所有被这个注解修饰的 Controller, 可以这么写:@ControllerAdvice(annotations= {CustomAnnotation.class})
这里不做任何规则限制,对所有的controller生效。
2、新建全局异常捕获类,注意与启动类在同包路径下,不然不生效需要在启动类中指定包路径。
import com.badao.demo.common.AjaxResult; import com.badao.demo.constant.Constants; import com.badao.demo.constant.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; //注意引入包的路径 import org.springframework.validation.BindException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e) { StackTraceElement stackTrace = e.getStackTrace()[0]; String methodName = stackTrace.getMethodName(); String fileName = stackTrace.getFileName(); String className = stackTrace.getClassName(); int lineNumber = stackTrace.getLineNumber(); log.error("异常发生在文件{}的类{}中的方法{}的第{}行',异常信息:{}", fileName,className,methodName,lineNumber,e.getMessage()); return AjaxResult.error(HttpStatus.ERROR, Constants.SERVER_ERROR); } }
这里使用@ExceptionHandler(Exception.class)
对所有的Exception进行捕获,然后统一返回封装的AjaxResult结果类
import com.badao.demo.constant.HttpStatus; import com.badao.demo.utils.StringUtils; import java.util.HashMap; /** * 操作消息提醒 * */ public class AjaxResult extends HashMap<String, Object> { private static final long serialVersionUID = 1L; /** 状态码 */ public static final String CODE_TAG = "code"; /** 返回内容 */ public static final String MSG_TAG = "msg"; /** 数据对象 */ public static final String DATA_TAG = "data"; /** * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 */ public AjaxResult() { } /** * 初始化一个新创建的 AjaxResult 对象 * * @param code 状态码 * @param msg 返回内容 */ public AjaxResult(int code, String msg) { super.put(CODE_TAG, code); super.put(MSG_TAG, msg); } /** * 初始化一个新创建的 AjaxResult 对象 * * @param code 状态码 * @param msg 返回内容 * @param data 数据对象 */ public AjaxResult(int code, String msg, Object data) { super.put(CODE_TAG, code); super.put(MSG_TAG, msg); if (StringUtils.isNotNull(data)) { super.put(DATA_TAG, data); } } /** * 返回成功消息 * * @return 成功消息 */ public static AjaxResult success() { return AjaxResult.success("操作成功"); } /** * 返回成功数据 * * @return 成功消息 */ public static AjaxResult success(Object data) { return AjaxResult.success("操作成功", data); } /** * 返回成功消息 * * @param msg 返回内容 * @return 成功消息 */ public static AjaxResult success(String msg) { return AjaxResult.success(msg, null); } /** * 返回成功消息 * * @param msg 返回内容 * @param data 数据对象 * @return 成功消息 */ public static AjaxResult success(String msg, Object data) { return new AjaxResult(HttpStatus.SUCCESS, msg, data); } /** * 返回错误消息 * * @return */ public static AjaxResult error() { return AjaxResult.error("操作失败"); } /** * 返回错误消息 * * @param msg 返回内容 * @return 警告消息 */ public static AjaxResult error(String msg) { return AjaxResult.error(msg, null); } /** * 返回错误消息 * * @param msg 返回内容 * @param data 数据对象 * @return 警告消息 */ public static AjaxResult error(String msg, Object data) { return new AjaxResult(HttpStatus.ERROR, msg, data); } /** * 返回错误消息 * * @param code 状态码 * @param msg 返回内容 * @return 警告消息 */ public static AjaxResult error(int code, String msg) { return new AjaxResult(code, msg, null); } }
然后状态码和提示消息是封装的常量类
状态码常量类
public class HttpStatus { /** * 操作成功 */ public static final int SUCCESS = 200; /** * 对象创建成功 */ public static final int CREATED = 201; /** * 请求已经被接受 */ public static final int ACCEPTED = 202; /** * 操作已经执行成功,但是没有返回数据 */ public static final int NO_CONTENT = 204; /** * 资源已被移除 */ public static final int MOVED_PERM = 301; /** * 重定向 */ public static final int SEE_OTHER = 303; /** * 资源没有被修改 */ public static final int NOT_MODIFIED = 304; /** * 参数列表错误(缺少,格式不匹配) */ public static final int BAD_REQUEST = 400; /** * 未授权 */ public static final int UNAUTHORIZED = 401; /** * 访问受限,授权过期 */ public static final int FORBIDDEN = 403; /** * 资源,服务未找到 */ public static final int NOT_FOUND = 404; /** * 不允许的http方法 */ public static final int BAD_METHOD = 405; /** * 资源冲突,或者资源被锁 */ public static final int CONFLICT = 409; /** * 不支持的数据,媒体类型 */ public static final int UNSUPPORTED_TYPE = 415; /** * 系统内部错误 */ public static final int ERROR = 500; /** * 接口未实现 */ public static final int NOT_IMPLEMENTED = 501; }
消息常量类
public class Constants { //请求响应常量 public static final String NO_API_CODE = "请求码不能为空"; public static final String ILLEGAL_API_CODE = "请求码不存在"; public static final String SERVER_ERROR = "服务器内部错误,请联系管理员!"; public static final String CALL_TOO_OFEN = "请求太频繁"; }
然后将异常发生的具体信息记录到日志中,异常的信息来源可以打断点获取和自定义需要获取的内容
2、这里是对Exception类型进行通配,如果需要对指定的异常类型或者自定义类型就行捕获,还可以
import com.badao.demo.common.AjaxResult; import com.badao.demo.constant.Constants; import com.badao.demo.constant.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; //注意引入包的路径 import org.springframework.validation.BindException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e) { StackTraceElement stackTrace = e.getStackTrace()[0]; String methodName = stackTrace.getMethodName(); String fileName = stackTrace.getFileName(); String className = stackTrace.getClassName(); int lineNumber = stackTrace.getLineNumber(); log.error("异常发生在文件{}的类{}中的方法{}的第{}行',异常信息:{}", fileName,className,methodName,lineNumber,e.getMessage()); return AjaxResult.error(HttpStatus.ERROR, Constants.SERVER_ERROR); } /** * 自定义验证异常 */ @ExceptionHandler(BindException.class) public AjaxResult handleBindException(BindException e) { log.error(e.getMessage(), e); String message = e.getAllErrors().get(0).getDefaultMessage(); return AjaxResult.error(HttpStatus.BAD_REQUEST, message); } /** * 自定义验证异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error(e.getMessage(), e); String message = e.getBindingResult().getFieldError().getDefaultMessage(); return AjaxResult.error(message); } }
这里注意引入包的路径,不然会出现不生效的情况。
这块还可参考
SpringBoot+@Validate+全局异常拦截实现自定义规则参数校验(校验get请求参数不能为空且在指定枚举类型中):
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1306054373、测试效果
手动编写一个除零异常并将controller中的try-catch去掉
接口统一响应,在日志中记录到具体信息
标签:AjaxResult,ControllerAdvice,SpringBoot,RestControlAdvice,final,static,msg,public From: https://www.cnblogs.com/badaoliumangqizhi/p/17390693.html