关键词:
前言:
在日常前后端分离的接口开发过程中,需要我们按相应的格式给前端返回响应的数据,常见的方式就是我们后端自己封装一个包装类,每次返回给前端数据的时候都需要我们自己手动构建一。
短时间内来看或许并没有什么,但是一旦接口量变大,我们每个接口都去构建返回值的话,那样就会浪费我们很多的开发时间,所以我们就可以对响应的内容进行统一的处理,在写Controller中的方法时我们也可以不用统一返回类型了。例如:
@ApiOperation(value = "分页查询告警信息") @PostMapping("findAlarmByPage") public PageInfo<TowerAlarmInfo> selectAll(@RequestBody AlarmReqVo alarmReqVo) return towerAlarmInfoService.findByPage(alarmReqVo);
这样可以让我们减少很多的工作量。 可能有的朋友就会问:那我们的非空验证可一些异常的处理怎么办呢? 下面介绍的就是统一的异常类型管理。把我们后端所有能发生的异常进行统一的封装(可以自定义一个异常类型),封装之后再返回给前端相应的提示,那么前端就会清晰的知道后端发生了什么错误,是不是前端的锅哈哈哈哈。
响应实体的封装:
首先肯定我们还是需要建一个统一返回的类:
package com.dlxx.tower.app.util; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor //这个注解表示变量为空的时候构造json就不带上这个变量 @JsonInclude(JsonInclude.Include.NON_NULL) public class FrontResult /** * 结果状态码 */ private Integer code; /** * 响应结果描述 */ private String message; /** * 返回数据 */ private Object data; public FrontResult(Object data) this.data = data; this.code = ResultEnum.SUCCESS.getCode(); this.message = "操作成功"; /** * 静态方法,返回前端实体结果 * * @param code 状态码 * @param message 消息 * @param data 数据 * @return 前端实体结果 */ public static FrontResult build(Integer code, String message, Object data) return new FrontResult(code, message, data); /** * 返回成功的结果实体 * * @param message 消息 * @param data 数据 * @return 实体 */ public static FrontResult getSuccessResult(String message, Object data) FrontResult result = new FrontResult(); result.code = ResultEnum.SUCCESS.getCode(); result.message = message; result.data = data; return result; /** * 返回无需data的成功结果实体 * * @param message 消息内容 * @return 返回结果 */ public static FrontResult getSuccessResultOnlyMessage(String message) FrontResult result = new FrontResult(); result.code = ResultEnum.SUCCESS.getCode(); result.message = message; result.data = null; return result; /** * 获取一个异常结果 * * @param code 错误码 * @param message 自定义异常信息 * @return FrontResult */ public static FrontResult getExceptionResult(Integer code, String message) FrontResult result = new FrontResult(); result.code = (code == null) ? ResultEnum.CODE_EXCEPTION.getCode() : code; result.message = message.isEmpty() ? ResultEnum.CODE_EXCEPTION.getMsg() : message; return result; /** * 得到异常结果 * * @param resultEnum 枚举结果代码 * @return @link FrontResult */ public static FrontResult getExceptionResult(ResultEnum resultEnum) FrontResult result = new FrontResult(); Integer code = resultEnum.getCode(); String msg = resultEnum.getMsg(); result.code = (code == null) ? ResultEnum.CODE_EXCEPTION.getCode() : code; result.message = msg.isEmpty() ? ResultEnum.CODE_EXCEPTION.getMsg() : msg; return result;
统一封装一下枚举类型ResultEnum
import lombok.AllArgsConstructor; /** * 结果枚举 * * @author longjun * @date 2023/04/11 */ @AllArgsConstructor public enum ResultEnum /** * 成功 */ SUCCESS(200, "操作成功"), /** * 代码异常 */ CODE_EXCEPTION(500, "后端代码内部异常"), /** * 参数错误 */ PARAMETER_ERROR(999, "前端入参异常"), /** * 失败 */ FAIL(1111, "后端代码异常异常"), /** * 空点 */ NULL_POINT(1000, "空指针异常"), /** * 指数误差 */ OUT_OF_INDEX_ERROR(1001, "索引越界异常"), /** * 模型零 */ MODEL_NULL(1002, "前端入参实体的实体为空"), /** * 数据库错误 */ DATABASE_ERROR(1003, "数据库异常"), /** * 身份验证错误 */ AUTHENTICATION_ERROR(1004, "身份验证异常"), /** * 逻辑错误 */ LOGIC_ERROR(1005, "业务逻辑异常"), /** * 类没有找到 */ CLASS_NOT_FOUND(1006, "类未找到异常"), /** * sql异常 */ SQL_EXCEPTION(1007, "sql语句异常"), /** * io例外 */ IO_EXCEPTION(1008, "io异常"), /** * json解析错误 */ JSON_PARSE_ERROR(1009, "json转换异常"), NUMBER_FORMAT_ERROR(1010, "String转换为数字错误"), /** * 更新失败 */ UPDATE_FAIL(1011, "更新失败"), /** * 发送POST错误 */ SEND_POST_ERROR(1012, "发送POST请求异常"), /** * 短信发送错误 */ SMS_SEND_ERROR(1013, "短信发送失败"); /** * 状态码 */ private Integer code; public Integer getCode() return code; ResultEnum(Integer code) this.code = code; private String msg; public String getMsg() return msg;
基本的类型封装好了,接下来就是重头戏,关键代码也在这里。主要就是实现了ResponseBodyAdvice接口,其实是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。
其实也就是采用了AOP的思想,对返回值进行一次修改。
该接口一共有两个方法:
(1)supports —— 判断是否要执行beforeBodyWrite方法,true为执行,false不执行 —— 通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。
(2)beforeBodyWrite —— 对 response 处理的具体执行方法。
package com.dlxx.tower.app.config; import com.dlxx.tower.app.exception.BizException; import com.dlxx.tower.app.util.FrontResult; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /** * 响应控制器建议 * * @author longjun * @description: 全局处理增强版Controller,避免Controller里返回数据每次都要用响应体来包装 * * @date 2023/04/04 */ @RestControllerAdvice(basePackages = "com.dlxx.tower.app.controller") public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false return !returnType.getGenericParameterType().equals(FrontResult.class); @Override public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) // String类型不能直接包装,所以要进行些特别的处理 if (returnType.getGenericParameterType().equals(String.class)) ObjectMapper objectMapper = new ObjectMapper(); try // 将数据包装在ResultVO里后,再转换为json字符串响应给前端 return objectMapper.writeValueAsString(new FrontResult(data)); catch (JsonProcessingException e) throw new BizException(); // 将原本的数据包装在ResultVO里 return new FrontResult(data);
这样基本上就可以对返回值进行统一封装了,下面就开始介绍全局异常;
统一异常处理:
首先自定义一个异常类型
package com.dlxx.tower.app.exception; import com.dlxx.tower.app.util.FrontResult; import com.dlxx.tower.app.util.ResultEnum; /** * 业务异常 * 自定义一个异常类,用于处理我们发生的业务异常 * * @author longjun * @version 1.0.0 * @date 2023/04/04 */ public class BizException extends RuntimeException private static final long serialVersionUID = 1L; /** * 错误码 */ protected Integer errorCode; /** * 错误信息 */ protected String errorMsg; public BizException() super(); public BizException(FrontResult errorInfoInterface) super(errorInfoInterface.getCode().toString()); this.errorCode = errorInfoInterface.getCode(); this.errorMsg = errorInfoInterface.getMessage(); public BizException(FrontResult errorInfoInterface, Throwable cause) super(errorInfoInterface.getCode().toString(), cause); this.errorCode = errorInfoInterface.getCode(); this.errorMsg = errorInfoInterface.getMessage(); public BizException(String errorMsg) super(errorMsg); this.errorMsg = errorMsg; public BizException(Integer errorCode, String errorMsg) super(String.valueOf(errorCode)); this.errorCode = errorCode; this.errorMsg = errorMsg; public BizException(ResultEnum resultEnum) super(String.valueOf(resultEnum.getCode())); this.errorCode = resultEnum.getCode(); this.errorMsg = resultEnum.getMsg(); public BizException(Integer errorCode, String errorMsg, Throwable cause) super(String.valueOf(errorCode), cause); this.errorCode = errorCode; this.errorMsg = errorMsg; public Integer getErrorCode() return errorCode; public void setErrorCode(Integer errorCode) this.errorCode = errorCode; public String getErrorMsg() return errorMsg; public void setErrorMsg(String errorMsg) this.errorMsg = errorMsg; @Override public String getMessage() return errorMsg; @Override public Throwable fillInStackTrace() return this;
然后就是异常处理类
package com.dlxx.tower.app.config; import com.dlxx.tower.app.exception.BizException; import com.dlxx.tower.app.util.FrontResult; import com.dlxx.tower.app.util.ResultEnum; import com.fasterxml.jackson.core.JsonParseException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.sql.SQLException; /** * 全局异常处理程序 * <p> * 统一异常处理 * 使用该注解表示开启了全局异常的捕获 * * @author longjun * @version 1.0.0 * @date 2023/04/04 */ @RestControllerAdvice @Slf4j public class GlobalExceptionHandler /** * 处理自定义的业务异常 * * @param req * @param e * @return */ @ExceptionHandler(value = BizException.class) @ResponseBody public FrontResult bizExceptionHandler(HttpServletRequest req, BizException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生业务异常!原因是:", e.getErrorMsg()); return FrontResult.getExceptionResult(e.getErrorCode(), e.getErrorMsg()); /** * 处理空指针的异常 * * @param req * @param e * @return */ @ExceptionHandler(value = NullPointerException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生空指针异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.NULL_POINT); /** * 处理索引越界异常 * * @param req * @param e * @return */ @ExceptionHandler(value = IndexOutOfBoundsException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("索引越界异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.OUT_OF_INDEX_ERROR); /** * 处理类未找到异常 * * @param req * @param e * @return */ @ExceptionHandler(value = ClassNotFoundException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生类未找到异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.CLASS_NOT_FOUND); /** * 处理SQL异常 * * @param req * @param e * @return */ @ExceptionHandler(value = SQLException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, SQLException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生SQL异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.SQL_EXCEPTION); /** * 处理IO异常 * * @param req * @param e * @return */ @ExceptionHandler(value = IOException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, IOException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生IO异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.IO_EXCEPTION); /** * json转换异常处理程序 * * @param req 要求事情 * @param e e * @return @link FrontResult */ @ExceptionHandler(value = JsonParseException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, JsonParseException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生JSON转换异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.JSON_PARSE_ERROR); /** * String转数字异常处理程序 * * @param req 要求事情 * @param e e * @return @link FrontResult */ @ExceptionHandler(value = NumberFormatException.class) @ResponseBody public FrontResult exceptionsHandler(HttpServletRequest req, NumberFormatException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生String转数字异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.NUMBER_FORMAT_ERROR); /** * 前端参数不匹配异常处理程序 * * @param req 要求事情 * @param e e * @return @link FrontResult */ @ExceptionHandler(value = HttpMessageNotReadableException.class) @ResponseBody public FrontResult exceptionsHandler(HttpServletRequest req, HttpMessageNotReadableException e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("发生前端参数不匹配异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.PARAMETER_ERROR); /** * 处理其他异常 * * @param req * @param e * @return */ @ExceptionHandler(value = Exception.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, Exception e) log.error("URL : " + req.getRequestURL().toString()); log.error("HTTP_METHOD : " + req.getMethod()); log.error("未知异常!原因是:", e); return FrontResult.getExceptionResult(ResultEnum.FAIL);
这里就已经可以实现这两个功能了
需要我们触发异常的时候就可以直接这样用
throw new BizException(ResultEnum.AUTHENTICATION_ERROR.getCode(), "验证码校验失败。");
第一个参数就是我们封装的枚举,第二个就是自定义的信息,当然也可以直接用枚举,这些都可以自己修改的。
springboot统一处理返回实体与异常抛出(代码片段)
当返回异常时,是这样子的"timestamp":"2019-12-11T05:37:10.096+0000","status":500,"error":"InternalServerError","message":"报错了","path":"/test/testException"但是,可能有时前台需要一个... 查看详情
springboot统一处理返回实体与异常抛出(代码片段)
当返回异常时,是这样子的"timestamp":"2019-12-11T05:37:10.096+0000","status":500,"error":"InternalServerError","message":"报错了","path":"/test/testException"但是,可能有时前台需要一个... 查看详情
springboot统一参数校验统一异常统一响应,这才是优雅的处理方式!(代码片段)
前言本篇主要要介绍的就是controller层的处理,一个完整的后端请求由4部分组成:接口地址(也就是URL地址)请求方式(一般就是get、set,当然还有put、delete)请求数据(request,有head跟body)响应数据(response)本篇将解决以... 查看详情
springboot项目如何做到统一异常处理
...个项目中对于异常的处理就显得尤为重要.那么,小编就以SpringBoot框架,通过代码实例展示统一异常的处理方式.1.首先我们简单搭建一个SpringBoot框架的项目,项目名称是exceptionhandler(异常处理)2.导入 查看详情
springboot项目统一结果,统一异常,统一日志,写的太好了。。(代码片段)
作者:永动的图灵机链接:https://juejin.cn/post/6844904033488994317统一结果返回目前的前后端开发大部分数据的传输格式都是json,因此定义一个统一规范的数据格式有利于前后端的交互与UI的展示。统一结果的一般形式是否... 查看详情
面试官|springboot项目如何统一结果,统一异常,统一日志?
点击上方关注“终端研发部”设为“星标”,和你一起掌握更多数据库知识作者:永动的图灵机链接:https://juejin.cn/post/6844904033488994317统一结果返回目前的前后端开发大部分数据的传输格式都是json,因此定义一个... 查看详情
springboot返回统一的json标准格式(代码片段)
自定义状态码枚举类封装返回结果全局异常捕获处理,使用@RestControllerAdvice注解拦截Controller方法的返回值,统一处理返回值/响应体创建Controller,准备测试请求接口,查看响应结果近年来,随着移动互联网... 查看详情
springboot返回统一的json标准格式(代码片段)
自定义状态码枚举类封装返回结果全局异常捕获处理,使用@RestControllerAdvice注解拦截Controller方法的返回值,统一处理返回值/响应体创建Controller,准备测试请求接口,查看响应结果近年来,随着移动互联网... 查看详情
大厂工程师如何给springboot封装响应数据和异常处理?
...从而减少沟通成本等。这篇文章,就带大家了解一下基于SpringBoot框架来封装返回报文以及统一异常处理。报文基本格式一般报文格式通常会包含状态码、状态描述(或错误提示信息)、业务数据等信息。 在此基础上,不同... 查看详情
springboot:如何优雅地进行响应数据封装异常处理?(代码片段)
...减少沟通成本等。这篇文章,就带大家了解一下基于SpringBoot框架来封装返回报文以及统一异常处理。报文基本格式一般报文格式通常会包含状态码、状态描述(或错误提示信息) 查看详情
springboot:如何优雅地进行响应数据封装异常处理?(代码片段)
...减少沟通成本等。这篇文章,就带大家了解一下基于SpringBoot框架来封装返回报文以及统一异常处理。报文基本格式一般报文格式通常会包含状态码、状态描述(或错误提示信息) 查看详情
springboot:如何优雅地进行响应数据封装异常处理?(代码片段)
...减少沟通成本等。这篇文章,就带大家了解一下基于SpringBoot框架来封装返回报文以及统一异常处理。报文基本格式一般报文格式通常会包含状态码、状态描述(或错误提示信息) 查看详情
springboot返回统一数据格式及其原理浅析
大家都知道,前后分离之后,后端响应最好以统一的格式的响应.譬如:名称描述 status状态码,标识请求成功与否,如[1:成功;-1:失败] errorCode错误码,给出明确错误码,更好的应对业务异常;请求成功该值可为空&nbs... 查看详情
springboot全局异常统一处理反参标准化
对于日常的开发过程中出现的异常,我把它分为两种,一种是需要给前端返回的异常,这种异常通常有入参格式、字段缺少、以及相关的业务异常,需要明确的告诉前端出现了什么问题,前端才好处理,而另一种异常例如空指针... 查看详情
springboot统一返回处理出现cannotbecasttojava.lang.string异常(代码片段)
SpringBoot统一返回处理出现cannotbecasttojava.lang.String异常一问题出现背景:二解决方案三异常原因分析原因:源码详细分析:正常返回:返回为字符串异常一问题出现背景:在使用@RestControllerAdvice和实现ResponseB... 查看详情
java服务器项目风格推荐-统一异常处理
...的一部分,快速的响应给用户处理结果2.使用我是用的是SpringBoot框架,框架自带了异常处理的方法,我只需要添加自己的异常处理函数就行,同时,配合上一章介绍的统一响应是异常处理更加完善。@ControllerAdvicepublicclassExceptionCo... 查看详情
springboot拦截器使用和常用功能统一封装(代码片段)
文章目录1.拦截器1.1拦截器的使用1.2拦截器的原理2.用户登录权限校验3.统一异常处理4.统一数据返回格式1.拦截器1.1拦截器的使用Spring中提供了拦截器HandlerInteceptor,它的具体使用分为以下两个步骤:创建自定义拦截器ÿ... 查看详情
springboot统一结果封装(代码片段)
ResultVo,返回结果对象@DatapublicclassResultVo<T>privateIntegercode;privateStringmessage;privateTdata;ResultVoUtil,封装返回结果publicclassResultVoUtilpublicstatic<T>ResultVo<T>sucess(Tdata)Re 查看详情