【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
前言
缘由
博友的需求就是我最大的动力
博友一说话,本狗笑哈哈。博友要我写啥,我就写啥。
特来一篇关于SpringBoot接口返回结果及异常统一处理,虽说封不封装都能用,但咱后端也得给前端小姐姐留个好印象不是。项目前后端分离,规范的数据传输格式,让REST风格的API具有简单、易读、易用的特点。不仅代码优美,也可以让带刀的前端小姐姐变得更漂亮。以下例子参考多个实际项目,最终总结来跟大家进行分享,大佬勿喷。
主要目标
实现2大重点
1. 统一接口返回结果
2. 配置全局异常处理
正文
一.统一接口返回结果
前端接口请求后台端,后端将返回结果统一封装。提高交互的规范性及通用性,也提高了前后端联调效率。前端根据规范格式返回结构体进行统一映射处理,就避免一个接口一个返回格式的问题。
1.统一封装结果包含如下参数
- 状态码:code
- 状态信息:status
- 返回信息:message
- 数据:data
2.统一封装结果包含如下方法
- 全参数方法
- 成功返回(无参)
- 成功返回(枚举)
- 成功返回(状态码+返回信息)
- 成功返回(返回信息 + 数据)
- 成功返回(状态码+返回信息+数据)
- 成功返回(数据)
- 成功返回(返回信息)
- 失败返回(无参)
- 失败返回(枚举)
- 失败返回(状态码+返回信息)
- 失败返回(返回信息+数据)
- 失败返回(状态码+返回信息+数据)
- 失败返回(数据)
- 失败返回(返回信息)
3.ResponseResult封装返回结果代码
package net.javadog.common.result;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.javadog.common.enums.HttpStatusEnum;
/**
* 返回结果集
*
* @author javadog
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("统一结果集处理器")
public class ResponseResult<T> {
/**
* 状态码
*/
@ApiModelProperty(value = "状态码")
private Integer code;
/**
* 状态信息
*/
@ApiModelProperty(value = "状态信息")
private Boolean status;
/**
* 返回信息
*/
@ApiModelProperty(value = "返回信息")
private String message;
/**
* 数据
*/
@ApiModelProperty(value = "数据")
private T data;
/**
* 全参数方法
*
* @param code 状态码
* @param status 状态
* @param message 返回信息
* @param data 返回数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
private static <T> ResponseResult<T> response(Integer code, Boolean status, String message, T data) {
ResponseResult<T> responseResult = new ResponseResult<>();
responseResult.setCode(code);
responseResult.setStatus(status);
responseResult.setMessage(message);
responseResult.setData(data);
return responseResult;
}
/**
* 全参数方法
*
* @param code 状态码
* @param status 状态
* @param message 返回信息
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
private static <T> ResponseResult<T> response(Integer code, Boolean status, String message) {
ResponseResult<T> responseResult = new ResponseResult<>();
responseResult.setCode(code);
responseResult.setStatus(status);
responseResult.setMessage(message);
return responseResult;
}
/**
* 成功返回(无参)
*
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success() {
return response(HttpStatusEnum.SUCCESS.getCode(), true, HttpStatusEnum.SUCCESS.getMessage(), null);
}
/**
* 成功返回(枚举参数)
*
* @param httpResponseEnum 枚举参数
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success(HttpStatusEnum httpResponseEnum) {
return response(httpResponseEnum.getCode(), true, httpResponseEnum.getMessage());
}
/**
* 成功返回(状态码+返回信息)
*
* @param code 状态码
* @param message 返回信息
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success(Integer code, String message) {
return response(code, true, message);
}
/**
* 成功返回(返回信息 + 数据)
*
* @param message 返回信息
* @param data 数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success(String message, T data) {
return response(HttpStatusEnum.SUCCESS.getCode(), true, message, data);
}
/**
* 成功返回(状态码+返回信息+数据)
*
* @param code 状态码
* @param message 返回信息
* @param data 数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success(Integer code, String message, T data) {
return response(code, true, message, data);
}
/**
* 成功返回(数据)
*
* @param data 数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success(T data) {
return response(HttpStatusEnum.SUCCESS.getCode(), true, HttpStatusEnum.SUCCESS.getMessage(), data);
}
/**
* 成功返回(返回信息)
*
* @param message 返回信息
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> success(String message) {
return response(HttpStatusEnum.SUCCESS.getCode(), true, message, null);
}
/**
* 失败返回(无参)
*
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail() {
return response(HttpStatusEnum.ERROR.getCode(), false, HttpStatusEnum.ERROR.getMessage(), null);
}
/**
* 失败返回(枚举)
*
* @param httpResponseEnum 枚举
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail(HttpStatusEnum httpResponseEnum) {
return response(httpResponseEnum.getCode(), false, httpResponseEnum.getMessage());
}
/**
* 失败返回(状态码+返回信息)
*
* @param code 状态码
* @param message 返回信息
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail(Integer code, String message) {
return response(code, false, message);
}
/**
* 失败返回(返回信息+数据)
*
* @param message 返回信息
* @param data 数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail(String message, T data) {
return response(HttpStatusEnum.ERROR.getCode(), false, message, data);
}
/**
* 失败返回(状态码+返回信息+数据)
*
* @param code 状态码
* @param message 返回消息
* @param data 数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail(Integer code, String message, T data) {
return response(code, false, message, data);
}
/**
* 失败返回(数据)
*
* @param data 数据
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail(T data) {
return response(HttpStatusEnum.ERROR.getCode(), false, HttpStatusEnum.ERROR.getMessage(), data);
}
/**
* 失败返回(返回信息)
*
* @param message 返回信息
* @param <T> 泛型
* @return {@link ResponseResult<T>}
*/
public static <T> ResponseResult<T> fail(String message) {
return response(HttpStatusEnum.ERROR.getCode(), false, message, null);
}
}
4.HttpStatusEnum返回结果代码
package net.javadog.common.enums;
import lombok.Getter;
/**
* Http状态返回枚举
*
* @author javadog
**/
@Getter
public enum HttpStatusEnum {
/**
* 操作成功
*/
SUCCESS(200, "操作成功"),
/**
* 对象创建成功
*/
CREATED(201, "对象创建成功"),
/**
* 请求已经被接受
*/
ACCEPTED(202, "请求已经被接受"),
/**
* 操作已经执行成功,但是没有返回数据
*/
NO_CONTENT(204, "操作已经执行成功,但是没有返回数据"),
/**
* 资源已被移除
*/
MOVED_PERM(301, "资源已被移除"),
/**
* 重定向
*/
SEE_OTHER(303, "重定向"),
/**
* 资源没有被修改
*/
NOT_MODIFIED(304, "资源没有被修改"),
/**
* 参数列表错误(缺少,格式不匹配)
*/
BAD_REQUEST(400, "参数列表错误(缺少,格式不匹配)"),
/**
* 未授权
*/
UNAUTHORIZED(401, "未授权"),
/**
* 访问受限,授权过期
*/
FORBIDDEN(403, "访问受限,授权过期"),
/**
* 资源,服务未找到
*/
NOT_FOUND(404, "资源,服务未找!"),
/**
* 不允许的http方法
*/
BAD_METHOD(405, "不允许的http方法"),
/**
* 资源冲突,或者资源被锁
*/
CONFLICT(409, "资源冲突,或者资源被锁"),
/**
* 不支持的数据,媒体类型
*/
UNSUPPORTED_TYPE(415, "不支持的数据,媒体类型"),
/**
* 系统内部错误
*/
ERROR(500, "系统内部错误"),
/**
* 接口未实现
*/
NOT_IMPLEMENTED(501, "接口未实现"),
/**
* 系统警告消息
*/
WARN(601,"系统警告消息");
private final Integer code;
private final String message;
HttpStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
5.SysLoginController实操调试
/**
* 登录验证
*
* @author javadog
*/
@RestController
public class SysLoginController {
@Resource
private SysLoginService loginService;
/**
* 登录方法
*
* @param loginRequest 登录信息
* @return 结果
*/
@PostMapping("/login")
public ResponseResult login(@RequestBody LoginRequest loginRequest) {
// 生成令牌
String token = loginService.login(loginRequest.getUsername(), loginRequest.getPassword());
return ResponseResult.success(token);
}
}
二.配置全局异常处理
在使用上方统一返回结果的加持下,规范的同时也不可避免程序异常情况。因此我们必须提前定义一个统一全局异常来捕获这些异常信息,并将其当作一种结果返回给控制层,友好的处理异常信息。
1.全局异常处理注解
@RestControllerAdvice
@RestControllerAdvice什么是?
@RestControllerAdvice注解是Spring MVC和Spring Boot应用程序中用于定义全局异常处理类的注解,它是@ControllerAdvice注解的特殊版本,是一个组合注解,由@ControllerAdvice、@ResponseBody组成。
@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个组件,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
@RestControllerAdvice有什么特点?
- 注解@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。
- @RestControllerAdvice注解将作用于所有注解了@RequestMapping的控制器的方法上。
- @ExceptionHandler:用于指定异常处理方法,与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。
- @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
- @ModelAttribute:本来作用是绑定键值对到Model中,当与@ControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对
2.@ExceptionHandler常用异常拦截
- 权限校验异常:AccessDeniedException(spring-security中异常)
- 请求方式不支持:HttpRequestMethodNotSupportedException
- 业务异常:ServiceException(自己业务定义异常)
- 拦截未知的运行时异常:RuntimeException
- 系统异常:Exception
- 自定义验证异常:BindException
- 自定义验证异常:MethodArgumentNotValidException
3.全局异常处理代码
package net.javadog.common.exception;
import cn.hutool.core.util.ObjectUtil;
import net.javadog.common.enums.HttpStatusEnum;
import net.javadog.common.result.ResponseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* 异常处理 配置
*
* @author javadog
*/
@RestControllerAdvice
public class GlobalException {
private static final Logger log = LoggerFactory.getLogger(GlobalException.class);
/**
* 权限校验异常
*/
@ExceptionHandler(AccessDeniedException.class)
public ResponseResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return ResponseResult.fail(HttpStatusEnum.FORBIDDEN.getCode(), HttpStatusEnum.FORBIDDEN.getMessage());
}
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return ResponseResult.fail(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public ResponseResult handleServiceException(ServiceException e) {
log.error(e.getMessage(), e);
Integer code = e.getCode();
return ObjectUtil.isNotNull(code) ? ResponseResult.fail(code, e.getMessage()) : ResponseResult.fail(e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public ResponseResult handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return ResponseResult.fail(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public ResponseResult handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return ResponseResult.fail(e.getMessage());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public ResponseResult handleBindException(BindException e) {
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return ResponseResult.fail(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return ResponseResult.fail(message);
}
}
4.全局异常处理实操调试
总结
本文通过多个SpringBoot实际项目进行归纳整理,从统一接口返回结果和配置全局异常处理两个方面出发,介绍如何优雅的封装规范后端接口输出,详细刨析@RestControllerAdvice和@ExceptionHandler注解及使用方式,增加后端服务健壮性和与前端对接规范性,希望由此化繁为简,能够帮到博友分毫。
猜你想问
如何与狗哥联系进行探讨
关注公众号【JavaDog程序狗】
公众号回复【入群】或者【加入】,便可成为【程序员学习交流摸鱼群】的一员,问题随便问,牛逼随便吹。
此群优势:
- 技术交流随时沟通
- 任何私活资源免费分享
- 实时科技动态抢先知晓
- CSDN资源免费下载
- 本人一切源码均群内开源,可免费使用
2.踩踩狗哥博客
大家可以在里面留言,随意发挥,有问必答
猜你喜欢
文章推荐
【项目实战】SpringBoot+uniapp+uview2打造H5+小程序+APP入门学习的聊天小项目
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
【模块分层】还不会SpringBoot项目模块分层?来这手把手教你!
【ChatGPT】SpringBoot+uniapp+uview2对接OpenAI,带你开发玩转ChatGPT
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅的更多相关文章
- springboot接口返回封装与异常控制
首先,返回有两个状态,status和code status标识response的状态,有2个值:0成功,-1服务错误. code跟业务有关,可以有各种数值,99999服务未知异常,10000参数异常, ...
- django学习-17.如何提供一个规范的接口返回值
目录结构 1.前言 2.进行实际的一个完整流程操作 2.1.第一步:编写一个用于查询用户数据的视图函数 2.2.第二步:编写对应的一个url匹配规则 2.3.第三步:启动django项目[hellow ...
- springboot 接口返回数据时 net.sf.json.JSONNull["empty"]) 异常
@ResetController返回数据时出现异常 Could not write JSON: Object is null; nested exception is com.fasterxml.ja ...
- 第3章 springboot接口返回json 3-1 SpringBoot构造并返回一个json对象
数据的使用主要还是以JSON为主,我们不会去使用XML. 这个时候我们先不使用@RestController,我们使用之前SpringMVC的那种方式,就是@Controller. @Respons ...
- SpringBoot接口返回去掉空字段
返回的接口中存在值为null或者空的字段过滤掉 @Configuration public class JacksonConfig { @Bean @Primary @ConditionalOnMis ...
- 第3章 springboot接口返回json 3-2 Jackson的基本演绎法
@JsonIgnore private String password; @JsonFormat(pattern="yyyy-MM-dd hh:mm:ss a",locale=&q ...
- SpringBoot统一处理返回结果和异常情况
如果文章有帮助到你,还请点个赞或留下评论 原因 在springboot项目里我们希望接口返回的数据包含至少三个属性: code:请求接口的返回码,成功或者异常等返回编码,例如定义请求成功. messa ...
- SpringBoot接口 - 如何优雅的对接口返回内容统一封装?
在以SpringBoot开发Restful接口时,统一返回方便前端进行开发和封装,以及出现时给出响应编码和信息.@pdai SpringBoot接口 - 如何优雅的对接口返回内容统一封装? RESTf ...
- Springboot项目全局异常统一处理
转自https://blog.csdn.net/hao_kkkkk/article/details/80538955 最近在做项目时需要对异常进行全局统一处理,主要是一些分类入库以及记录日志等,因为项 ...
- SpringBoot接口 - 如何优雅的写Controller并统一异常处理?
SpringBoot接口如何对异常进行统一封装,并统一返回呢?以上文的参数校验为例,如何优雅的将参数校验的错误信息统一处理并封装返回呢?@pdai 为什么要优雅的处理异常 如果我们不统一的处理异常,经 ...
随机推荐
- Docker-Compose快速搭建LNMP
Docker-Compose 1.安装Docker sudo apt -y install docker.io docker version 查看版本号 docker help 查看帮助文档 2.更换 ...
- 【C#/.NET】xUnit和Moq实现TDD
目录 前置条件 Moq xUnit TDD 实践 创建项目 红灯 绿灯 重构 单元测试一些最佳实践 总结 前置条件 Moq 安装Moq包 Install-Package Moq Moq是一个Mo ...
- 认识Dubbo与RPC
关注王有志,分享硬核Java技术的互金摸鱼侠 加入Java人的提桶跑路群:共同富裕的Java人 开个新坑,和大家一起学习Dubbo 3.X.我们按照一个由浅入深顺序来学习,先从使用Dubbo开始,再深 ...
- 从Kafka中学习高性能系统如何设计
1 前言 相信各位小伙伴之前或多或少接触过消息队列,比较知名的包含Rocket MQ和Kafka,在京东内部使用的是自研的消息中间件JMQ,从JMQ2升级到JMQ4的也是带来了性能上的明显提升,并且J ...
- 什么是 CSR、SSR、SSG、ISR - 渲染模式详解
本文以 React.Vue 为例,介绍下主流的渲染模式以及在主流框架中如何实现上述的渲染模式. 前置知识介绍 看渲染模式之前我们先看下几个主流框架所提供的相关能力,了解的可跳到下个章节. 挂载组件到 ...
- 选择排序与冒泡排序(c语言+Java语言)
选择排序 O(n2) 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾. 重复第二步,直到所有元素均排序完毕. 例 ...
- redis雪崩问题解决
缓存雪崩 出现的场景 缓存服务器宕机,没有设置持久化 介绍:缓存服务器宕机,没有设置持久化,导致缓存数据全部丢失,请求全部转发到数据库,造成数据库短时间内承受大量请求而崩掉. 缓存集中失效 缓存的ke ...
- LeetCode 周赛(2023/07/08)渐入佳境
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问. 往期回顾:LeetCode 单周赛第 351 场 · 一场关于子数 ...
- 基于GPT搭建私有知识库聊天机器人(四)问答实现
前文链接: 基于GPT搭建私有知识库聊天机器人(一)实现原理 基于GPT搭建私有知识库聊天机器人(二)环境安装 基于GPT搭建私有知识库聊天机器人(三)向量数据训练 在前面的文章中,我们介绍了如何使用 ...
- 详解同为4800W像素的相机传感器,三星GM1和索尼IMX586区别在哪里?
数字影像之父Bryce Bayer基于RGB模式,通过在感光元件前加上一个滤镜的方法终于实现了彩色照片.Bayer滤镜跨出了照片从黑白到彩色的一大步,但是对于挑剔的人眼来说,每个像素只有一个颜色是远远 ...