@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浏览器访问的 ...
随机推荐
- 2022春每日一题:Day 21
题目:[SCOI2007]降雨量 这题比较坑,分几种情况,但是可以总起来说,分开写,两个月份都没出现,maybe,否则如果两个月份都大于[l+1,r-1]的最大值,如果两个月份差值=r-l输出,tru ...
- 论文复现|Panoptic Deeplab(全景分割PyTorch)
摘要:这是发表于CVPR 2020的一篇论文的复现模型. 本文分享自华为云社区<Panoptic Deeplab(全景分割PyTorch)>,作者:HWCloudAI . 这是发表于CVP ...
- JSON提取器中串联一个接口的多个值传给下个接口(compute concatenation var的实际使用场景)
实际场景:某功能在前端支持选择多条数据后点击提交,表现在接口是,一次传了多个Id. 问题:需要将上个接口的多个Id串联,传给提交接口. 处理方式:通过JSON提取器勾选:compute concate ...
- 12V转5V降压芯片,12V转3.3V稳压芯片电路图
12V转5V应用中,大多要求会输出电流高的,稳压LDO就不能满足了,需要使用DC-DC降压芯片来持续稳压5V,输出电流1000MA,2000MA,3000MA,5000MA等.不同的输出电流可以选择适 ...
- 【java】【File】用File相关类写一个小工具,完成从指定目录下抽取指定文件并复制到新路径下完成重命名的功能
今日份代码: import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import java.io.*; i ...
- Vue快速上门(1)-基础知识图文版
VUE家族系列: Vue快速上门(1)-基础知识 Vue快速上门(2)-模板语法 Vue快速上门(3)-组件与复用 01.基本概念 1.1.先了解下MVVM VUE是基于MVVM思想实现的,那什么是M ...
- python基础(数据库、可视化软件Navicat、python操作MySQL)
多表查询的两种方法 数据准备: 建表 create table dep( id int primary key auto_increment, name varchar(20) ); create t ...
- 对象和类—Java世界的细胞
对象和类-Java世界的细胞 今天向大家介绍我自己关于Java中对象和类的一些体会,中有不足还请大家多多指教 1.面向对象程序设计 为什么会产生面向对象 我认为人们在最初探索计算机世界时,常常会从计算 ...
- 基于容器的PaaS混合云的几种形式
概述 这是 Gartner 的一个图,提供了全球的基于容器的 PaaS 公有云.混合云服务的梳理展示: 这里提供一个其他的视角: 中国市场,基于容器的 PaaS 混合云(公有云 + 私有云)的相关厂商 ...
- 学习.NET MAUI Blazor(二)、MAUI是个啥
随着.NET 7的发布,MAUI也正式发布了.那么MAUI是个啥?我们先来看看官方解释: .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移 ...