springboot统一响应实体封装+统一异常类管理(代码片段)

一只搬砖的小松鼠 一只搬砖的小松鼠     2023-04-18     212

关键词:

前言:

  在日常前后端分离的接口开发过程中,需要我们按相应的格式给前端返回响应的数据,常见的方式就是我们后端自己封装一个包装类,每次返回给前端数据的时候都需要我们自己手动构建一。

短时间内来看或许并没有什么,但是一旦接口量变大,我们每个接口都去构建返回值的话,那样就会浪费我们很多的开发时间,所以我们就可以对响应的内容进行统一的处理,在写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 查看详情