SpringBoot中统一接口返回与全局异常的处理
发表于:2025-02-09 作者:千家信息网编辑
千家信息网最后更新 2025年02月09日,本篇内容介绍了"SpringBoot中统一接口返回与全局异常的处理"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能
千家信息网最后更新 2025年02月09日SpringBoot中统一接口返回与全局异常的处理背景
统一接口返回
定义API返回码枚举类
全局异常处理
本篇内容介绍了"SpringBoot中统一接口返回与全局异常的处理"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
目录
背景
统一接口返回
定义API返回码枚举类
定义正常响应的API统一返回体
定义异常响应的API统一返回体
编写包装返回结果的自定义注解
定义返回结果拦截器
WebMvc配置类拦截器注册者添加返回结果拦截器
编写响应体处理器
接口调用
测试结果
全局异常处理
编写自定义异常基类
编写自定义业务异常类
定义全局异常处理类
接口调用
测试结果
背景
在分布式、微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式。前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。维护一套完善且规范的接口是非常有必要的, 这样不仅能够提高对接效率,也可以让我的代码看起来更加简洁优雅。
使用统一返回结果时,还有一种情况,就是程序的报错是由于运行时异常导致的结果,有些异常是我们在业务中抛出的,有些是无法提前预知。
因此,我们需要定义一个统一的全局异常,在Controller捕获所有异常,并且做适当处理,并作为一种结果返回。
统一接口返回
定义API返回码枚举类
public enum ResultCode { /* 成功状态码 */ SUCCESS(200, "成功"), /* 错误状态码 */ NOT_FOUND(404, "请求的资源不存在"), INTERNAL_ERROR(500, "服务器内部错误"), PARAMETER_EXCEPTION(501, "请求参数校验异常"), /* 业务状态码 */ USER_NOT_EXIST_ERROR(10001, "用户不存在"), ; private Integer code; private String message; public Integer code() { return this.code; } public String message() { return this.message; } ResultCode(Integer code, String message) { this.code = code; this.message = message; }}
定义正常响应的API统一返回体
@Datapublic class Resultimplements Serializable {private Integer code; private String message; private boolean success = true; private T data; @JsonIgnore private ResultCode resultCode; private Result() { } public void setResultCode(ResultCode resultCode) { this.resultCode = resultCode; this.code = resultCode.code(); this.message = resultCode.message(); } public Result(ResultCode resultCode, T data) { this.code = resultCode.code(); this.message = resultCode.message(); this.data = data; } public static Result success() { Result result = new Result<>(); result.setResultCode(ResultCode.SUCCESS); return result; } public static Result success(T data) { Result result = new Result<>(); result.setResultCode(ResultCode.SUCCESS); result.setData(data); return result; }}
定义异常响应的API统一返回体
@Datapublic class ErrorResult implements Serializable { private Integer code; private String message; private boolean success = false; @JsonIgnore private ResultCode resultCode; public static ErrorResult error() { ErrorResult result = new ErrorResult(); result.setResultCode(ResultCode.INTERNAL_ERROR); return result; } public static ErrorResult error(String message) { ErrorResult result = new ErrorResult(); result.setCode(ResultCode.INTERNAL_ERROR.code()); result.setMessage(message); return result; } public static ErrorResult error(Integer code, String message) { ErrorResult result = new ErrorResult(); result.setCode(code); result.setMessage(message); return result; } public static ErrorResult error(ResultCode resultCode, String message) { ErrorResult result = new ErrorResult(); result.setResultCode(resultCode); result.setMessage(message) return result; }}
编写包装返回结果的自定义注解
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.METHOD}) //作用于方法和类(接口)上@Documentedpublic @interface ResponseResult {}
定义返回结果拦截器
@Componentpublic class ResponseResultInterceptor implements HandlerInterceptor { /* 使用统一返回体的标识 */ private static final String RESPONSE_RESULT_ANNOTATION = "RESPONSE-RESULT-ANNOTATION"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 正在处理请求的方法bean if (handler instanceof HandlerMethod) { final HandlerMethod handlerMethod = (HandlerMethod) handler; // 获取当前类 final Class> clazz = handlerMethod.getBeanType(); // 获取当前方法 final Method method = handlerMethod.getMethod(); // 判断是否在类对象上加了注解 if (clazz.isAnnotationPresent(ResponseResult.class)) { // 设置该请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口进行判断 request.setAttribute(RESPONSE_RESULT_ANNOTATION, clazz.getAnnotation(ResponseResult.class)); } // 判断是否在方法上加了注解 else if (method.isAnnotationPresent(ResponseResult.class)) { // 设置该请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口进行判断 request.setAttribute(RESPONSE_RESULT_ANNOTATION, method.getAnnotation(ResponseResult.class)); } } return true; }}
WebMvc配置类拦截器注册者添加返回结果拦截器
@Configurationpublic class WebMvcConfig implements WebMvcConfigurer { /** * 添加自定义拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new ResponseResultInterceptor()).addPathPatterns("/**"); }}
编写响应体处理器
/** * 统一处理响应体,用Result.success静态方法包装, * 在API接口使用时就可以直接返回原始类型 */@RestControllerAdvicepublic class ResponseResultHandler implements ResponseBodyAdvice
接口调用
@Api("用户管理")@RestController@RequestMapping("user")@ResponseResult // 作用于类上,对所有接口有效public class UserController { @Autowired private UserService userService; @ResponseResult // 作用于方法上 @ApiOperation("根据ID查询用户") @GetMapping("one") public User selectOne(Long id) { // 由于在ResponseResultHandler中已经统一将返回数据用Result.success包装了, // 直接返回原始类型即可,代码更简洁 return this.userService.queryById(id); } @ResponseResult @ApiOperation("查询所有用户") @GetMapping("all") public ListselectAll(Page page) { // 由于在ResponseResultHandler中已经统一将返回数据用Result.success包装了, // 直接返回原始类型即可,代码更简洁 return this.userService.queryAllByLimit(page); }}
测试结果
全局异常处理
编写自定义异常基类
@Datapublic class BaseException extends RuntimeException { private static final int BASE_EXCEPTION_CODE = ResultCode.INTERNAL_ERROR.code(); private static final String BASE_EXCEPTION_MESSAGE = ResultCode.INTERNAL_ERROR.message(); private Integer code; private String message; public BaseException() { super(BASE_EXCEPTION_MESSAGE); this.code = BASE_EXCEPTION_CODE; this.message = BASE_EXCEPTION_MESSAGE; } public BaseException(String message) { super(message); this.code = BASE_EXCEPTION_CODE; this.message = message; } public BaseException(ResultCode resultCode) { super(resultCode.message()); this.code = resultCode.code(); this.message = resultCode.message(); } public BaseException(Throwable cause) { super(cause); this.code = BASE_EXCEPTION_CODE; this.message = BASE_EXCEPTION_MESSAGE; } public BaseException(String message, Throwable cause) { super(message, cause); this.code = BASE_EXCEPTION_CODE; this.message = message; } public BaseException(Integer code, String message) { super(message); this.code = code; this.message = message; } public BaseException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; this.message = message; }}
编写自定义业务异常类
public class BizException extends BaseException { public BizException(ResultCode resultCode) { super(resultCode); }}
定义全局异常处理类
通过@ExceptionHandler注解来统一处理某一类异常
@Slf4j@RestControllerAdvicepublic class GlobalExceptionHandler { /** * 统一处理自定义基础异常 */ @ExceptionHandler(BaseException.class) public ErrorResult baseException(BaseException e) { if (StringUtils.isEmpty(e.getCode())) { return ErrorResult.error(e.getMessage()); } return ErrorResult.error(e.getCode(), e.getMessage()); } /** * 统一处理自定义业务异常 */ @ExceptionHandler(BizException.class) public ErrorResult bizException(BizException e) { if (StringUtils.isEmpty(e.getCode())) { return ErrorResult.error(e.getMessage()); } return ErrorResult.error(e.getCode(), e.getMessage()); } /** * 统一处理非自定义异常外的所有异常 */ @ExceptionHandler(Exception.class) public ErrorResult handleException(Exception e) { log.error(e.getMessage(), e); return ErrorResult.error(e.getMessage()); } /** * 兼容Validation校验框架:忽略参数异常处理器 */ @ExceptionHandler(MissingServletRequestParameterException.class) public ApiResultparameterMissingExceptionHandler(MissingServletRequestParameterException e) { log.error(e.getMessage(), e); return ErrorResult.error(PARAMETER_EXCEPTION, "请求参数 " + e.getParameterName() + " 不能为空"); } /** * 兼容Validation校验框架:缺少请求体异常处理器 */ @ExceptionHandler(HttpMessageNotReadableException.class) public ErrorResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) { log.error(e.getMessage(), e); return ErrorResult.error(PARAMETER_EXCEPTION, "参数体不能为空"); } /** * 兼容Validation校验框架:参数效验异常处理器 */ @ExceptionHandler(MethodArgumentNotValidException.class) public ErrorResult parameterExceptionHandler(MethodArgumentNotValidException e) { log.error(e.getMessage(), e); // 获取异常信息 BindingResult exceptions = e.getBindingResult(); // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息 if (exceptions.hasErrors()) { List errors = exceptions.getAllErrors(); if (!errors.isEmpty()) { // 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可 FieldError fieldError = (FieldError) errors.get(0); return ErrorResult.error(PARAMETER_EXCEPTION, fieldError.getDefaultMessage()); } } return ErrorResult.error(PARAMETER_EXCEPTION, "请求参数校验异常"); }}
接口调用
@ResponseResult @GetMapping public User update() { // 非自定义的运行时异常 long id = 10 / 0; return userService.queryById(id); } @ResponseResult @PostMapping public User insert() { // 抛出自定义的基础异常 throw new BaseException(); } @ResponseResult @DeleteMapping public boolean delete() { // 抛出自定义的业务异常 throw new BizException(USER_NOT_EXIST_ERROR); }
测试结果
"SpringBoot中统一接口返回与全局异常的处理"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
处理
统一
接口
结果
参数
包装
全局
业务
拦截器
方法
处理器
注解
错误
框架
消息
用户
服务
测试
原始
简洁
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
静海有统计软件开发公司吗
时空软件开发
如何让数据库密码设置为长期
公文数据库制作
网络技术专业能报考一建吗
宝塔iis服务器管理
员工应遵守网络安全管理规定
叨叨网络安全大全
速达同时用一个数据库
国内外企业网络安全现状
陆军网络安全宣传标语
雁塔软件开发包装
美国360回应网络安全
落实网络安全等级保护工作
吉林大学网络安全考试题
怎么去除服务器密码
计算机网络技术基础知识分析
软件开发 bug跟踪软件
abap数据库显示字段
对电子网络技术的理解
泗阳购买网络技术诚信合作
质量网络技术咨询口碑推荐
珠海数据库脱敏
网络安全博士就业方向及待遇
日本科学技术社数据库
科密考勤机数据库安装
国家网络安全信息化座谈会
网络安全热门研究方向
平安科技 互联网金融
备注设计软件开发