@RestControllerAdvice全局异常统一处理
spring项目中,我们通常规定了返回的格式(成功-失败-异常),特别是异常怎么处理方便呢?
1.自定义状态码实体
package com.ruoyi.common.constant; /**
* 返回状态码
*
* @author ruoyi
*/
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;
}
2.创建返回实体
按照规定的格式创建返回实体,这样子就可以规范返回的格式-下面是一个自定义的返回实体
package com.ruoyi.common.core.domain; import java.util.HashMap;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils; /**
* 操作消息提醒
*
* @author ruoyi
*/
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);
}
}
3.使用@RestControllerAdvice创建全局处理器
@RestControllerAdvice是@ControllerAdvice和@ResponseBody的合并。此注解标记的类就是全局处理类,在这个类中可以自定义一个个的方法,用 @ExceptionHandler(异常类型)注解,那么它就回去拦截对应的异常,在该方法中进行处理,且把处理结果返回给页面。
3.1创建全局异常处理器
package com.ruoyi.framework.web.exception; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
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;
import org.springframework.web.servlet.NoHandlerFoundException;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.utils.StringUtils; /**
* 全局异常处理器
*
* @author ruoyi
*/
@RestControllerAdvice //标识这是全局异常处理器
public class GlobalExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); /**
* 基础异常
*/
@ExceptionHandler(BaseException.class) //表示对指定异常进行拦截并处理
public AjaxResult baseException(BaseException e)
{
return AjaxResult.error(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(CustomException.class)
public AjaxResult businessException(CustomException e)
{
if (StringUtils.isNull(e.getCode()))
{
return AjaxResult.error(e.getMessage());
}
return AjaxResult.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e)
{
log.error(e.getMessage(), e);
return AjaxResult.error(e.getMessage());
} }
注解1:@RestControllerAdvice //标识这是全局异常处理器
注解2:@ExceptionHandler(BaseException.class) //表示对指定异常进行拦截并处理。当我们抛出指定的异常(可以自定义异常)时,会被ExceptionHandler拦截,并进行处理
3.2上面使用了自定义的异常
package com.ruoyi.common.exception; /**
* 自定义异常
*
* @author ruoyi
*/
public class CustomException extends RuntimeException
{
private static final long serialVersionUID = 1L; private Integer code; private String message; public CustomException(String message)
{
this.message = message;
} public CustomException(String message, Integer code)
{
this.message = message;
this.code = code;
} public CustomException(String message, Throwable e)
{
super(message, e);
this.message = message;
} @Override
public String getMessage()
{
return message;
} public Integer getCode()
{
return code;
}
}
3.3业务中抛出异常
public String login(String username, String password, String code, String uuid)
{
//1.校验验证码
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; //缓存中存入验证码格式 {Constants.CAPTCHA_CODE_KEY + uuid:验证码}
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)
{ //缓存中没有验证码 发起一个异步任务-记录此次错误登录信息
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) //验证码错误 发起一个异步任务-记录此次错误登录信息
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
// 2.用户验证
Authentication authentication = null;
try
{
// 该方法会去调用package com.ruoyi.framework.web.service.UserDetailsServiceImpl.loadUserByUsername 若是校验失败,会抛出异常
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password)); //loadUserByUsername方法会获取用户,匹配密码是自动完成的
}
catch (Exception e)
{
if (e instanceof BadCredentialsException) //用户密码不匹配 发起一个异步任务-记录此次错误登录信息
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{ //其它不匹配 比如没有该用户 该用户被停用等等 发起一个异步任务-记录此次错误登录信息
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new CustomException(e.getMessage());
}
} //发起一个异步任务-记录此次成功登录信息
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 生成token
return tokenService.createToken(loginUser);
}
throw new CustomException(e.getMessage());
这里我们在service层就直接抛出异常CustomException,会被上面的全局异常处理器拦截,和@ExceptionHandler(CustomException.class)匹配,匹配到了下面的方法
/**
* 业务异常
*/
@ExceptionHandler(CustomException.class)
public AjaxResult businessException(CustomException e)
{
if (StringUtils.isNull(e.getCode()))
{
return AjaxResult.error(e.getMessage());
}
return AjaxResult.error(e.getCode(), e.getMessage());
}
抛出CustomException,该方法执行,最后返回一个AjaxResult对象给页面 ----return AjaxResult.error(e.getCode(), e.getMessage());
{
- code: 状态码
- msg: 返回内容
- data :数据对象
}
3.4说明
全局异常管理器实际上就是创建了拦截器对抛出的异常进行处理,并把处理结果返回给页面
上面我们使用的是@RestControllerAdvice而不是@ControllerAdvice,它是@ControllerAdvice和@ResponseBody的结合,返回的都是json数据。如果使用@ControllerAdvice,方法上需要添加@ResponseBody才能返回json格式数据。
@RestControllerAdvice全局异常统一处理的更多相关文章
- SpringMVC全局异常统一处理
SpringMVC全局异常统一处理以及处理顺序最近在使用SpringMVC做全局异常统一处理的时候遇到的问题,就是想把ajax请求和普通的网页请求分开返回json错误信息或者跳转到错误页. 在实际做的 ...
- Springboot项目全局异常统一处理
转自https://blog.csdn.net/hao_kkkkk/article/details/80538955 最近在做项目时需要对异常进行全局统一处理,主要是一些分类入库以及记录日志等,因为项 ...
- Android app 全局异常统一处理
异常处理需求 Android app 出现 crash 时,会出现 "程序异常退出" 的提示并关闭,体验不好,另外主要是无法知道哪里出现的崩溃,需要知道哪里造成的异常,就需要一个全 ...
- springBoot 全局异常方式处理自定义异常 @RestControllerAdvice + @ExceptionHandler
前言 本文讲解使用 @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理,可以处理大部分开发中用到的自自定义业务异常处理了,再也不用 ...
- Spring boot异常统一处理方法:@ControllerAdvice注解的使用、全局异常捕获、自定义异常捕获
一.全局异常 1.首先创建异常处理包和类 2.使用@ControllerAdvice注解,全局捕获异常类,只要作用在@RequestMapping上,所有的异常都会被捕获 package com.ex ...
- SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念: SpringBoot 错误处理 2.具体内容 在之前的程序里面如果一旦出现了错误之后就会出现一堆的大白板,这个白板会 ...
- @ControllerAdvice全局异常拦截
@ControllerAdvice 拦截异常并统一处理 在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder ...
- SpringBoot:如何优雅地处理全局异常?
之前用springboot的时候,只知道捕获异常使用try{}catch,一个接口一个try{}catch,这也是大多数开发人员异常处理的常用方式,虽然屡试不爽,但会造成一个问题,就是一个Contro ...
- springboot之全局处理统一返回
springboot之全局处理统一返回 简介 在REST风格的开发中,避免通常会告知前台返回是否成功以及状态码等信息.这里我们通常返回的时候做一次util的包装处理工作,如:Result类似的类,里面 ...
- SpringBoot 全局异常配置
在日常web开发中发生了异常,往往是需要通过一个统一的异常处理来保证客户端能够收到友好的提示. 一.默认异常机制 默认异常处理(SpringBoot 默认提供了两种机制,一种是针对于web浏览器访问的 ...
随机推荐
- CentOS 8 离线安装 podman 解决方法
CentOS 8 系统中如果没有安装Podman的话,想要离线安装会比较麻烦,因为podman依赖的包比较多,从网上一个一个下载会很繁琐,也容易出错. 这里介绍一种曲线救国的方式来离线安装. 首先分享 ...
- 如何精简 Prometheus 的指标和存储占用
前言 随着 Prometheus 监控的组件.数量.指标越来越多,Prometheus 对计算性能的要求会越来越高,存储占用也会越来越多. 在这种情况下,要优化 Prometheus 性能, 优化存储 ...
- vue项目中,alert使用
methods:{ sendData() { ··· // 中间省略的代码 alert(`发送成功,连接失败的号码有:${failList}`) // failList里存的是后台返回的数据 } }
- c#对接每人计平台获取数据
使用c#对接到晓舟科技的客流统计设备.那么需要先注册一个平台的账号 地址:http://mrd.meirenji.cn/login;JSESSIONID=323cbd18-29ed-4232-8c04 ...
- php中的try语句
为了进一步处理异常,我们需要使用try-catch语句----包括Try语句和至少一个的catch语句.任何调用 可能抛出异常的方法的代码都应该使用try语句.Catch语句用来处理可能抛出的异常.以 ...
- psutil.AccessDenied: psutil.AccessDenied
解决办法 import psutil for proc in psutil.process_iter(): try: print(proc.name()) except (psutil.NoSuchP ...
- Spring Boot整合log4j实战(一):排除自带依赖、日志重定向、测试类验证
〇.参考资料 1.springboot整合log4j全过程详解 https://blog.csdn.net/m0_60845963/article/details/123307232 2.Spring ...
- requests模块和openpyxl模块
第三方模块的下载和使用 1,第三方模块就是别人大神们已经写好的模块,功能特别强大.我们如果像使用第三方模块就先要进行下载.下载完成后 才可以在python中直接调用 2.下载方式一:pip工具 pip ...
- Java 中的接口还可以这样用,你知道吗?
Java 程序员都知道要面向接口编程,那 Java 中的接口除了定义接口方法之外还能怎么用你知道吗?今天阿粉就来带大家看一下 Java 中的接口还可以有哪些用法. 基本特性 我们先看一下接口的基本特性 ...
- 推荐一款 在线+离线数据 同步框架 Dotmim.Sync
移动智能应用可以分为在线模式.纯离线模式与"在线+离线"混合模式.在线模式下系统数据一般存储在服务器端的大中型数据库(如 SQL Server.Oracle.MySQL 等),移动 ...