java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot统一结果封装怎么做

SpringBoot统一结果封装怎么做?手把手教你实现

作者:桃吱咬咬_

还在为接口返回格式不统一而烦恼吗?本文详解SpringBoot项目中统一结果封装与全局异常处理的实战方法,涵盖自定义状态码、统一返回类、业务异常及404/405处理,让你的API更规范、更易维护

前言

统一结果封装可以带来以下几个好处:

{
    "code": "code value",
    "status": true or false,
    "message": "message value",
    "data": {}
}

这种返回格式主要包含四部分:

  1. code:状态码,由后端统一定义各种返回结果的状态码
  2. status:状态信息,用于标识请求是否成功
  3. message:返回信息,向客户端传递处理结果的描述
  4. data:实际数据,包含业务处理的具体结果

我们返回的信息至少包括code、message、data三部分,其中code是我们后端和前端约定好的状态码,message为返回信息,data为返回的实际数据,没有返回数据则为null。

除了这三部分外,你还可以定义一些其他字段,比如状态信息status,请求时间timestamp

一、统一结果集处理器

1. 自定义常用结果枚举类

/**
 * <p>
 * 常用结果枚举类
 * </p>
 *
 * @author Patrick
 * @since 2024-07-02
 */
@Getter
public enum HttpCodeEnum {

    //==================== 登录相关枚举 ======================
    /**
     * 登陆超时
     */
    RC100(100, "登陆超时!"),

    /**
     * 用户未登录
     */
    RC101(101, "用户未登录,请先进行登录!"),

    /**
     * 账户被禁用,请联系管理员解决
     */
    RC102(102, "账户被禁用,请联系管理员解决!"),

    /**
     * 用户信息加载失败
     */
    RC103(103, "用户信息加载失败!"),

    /**
     * 用户身份信息获取失败
     */
    RC104(104, "用户身份信息获取失败!"),

    /**
     * 用户名不能为空
     */
    RC105(105, "用户名不能为空!"),

    /**
     * 密码不能为空
     */
    RC106(106, "密码不能为空!"),

    /**
     * 用户名或密码错误
     */
    RC107(107, "用户名或密码错误!"),

    /**
     * 用户登录成功
     */
    RC108(108, "用户登录成功!"),

    /**
     * 用户注销成功
     */
    RC109(109, "用户注销成功!"),

    //==================== 注册相关枚举 ======================

    /**
     * 验证码错误
     */
    RC300(300, "验证码错误!"),

    /**
     * 验证码过期
     */
    RC301(301, "验证码已过期!"),

    /**
     * 用户名已存在
     */
    RC302(302, "用户名已存在!"),

    //======================= 其他枚举 ==============================

    /**
     * 参数格式不合法
     */
    RC400(400, "参数格式不合法,请检查后重试!"),
    
    /**
     * 没有权限
     */
    RC403(403, "您没有操作权限!"),

    /**
     * 页面不存在
     */
    RC404(404, "未找到您请求的资源!"),

    /**
     * 请求方式错误
     */
    RC405(405, "请求方式错误,请检查后重试!"),

    /**
     * 操作成功
     */
    SUCCESS(200, "操作成功!"),

    /**
     * 操作失败
     */
    ERROR(500, "操作失败!"),

    /**
     * 未知异常
     */
    RC600(600, "服务器繁忙或服务器错误,请稍后再试!");

    // 响应状态码
    private final Integer code;

    // 响应返回信息
    private final String message;

    HttpCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

}

该枚举类为我们和前端约定好的返回状态码和描述信息,可根据自己的需求修改状态码和返回信息

2. 自定义统一返回格式

/**
 * <p>
 * 统一结果集处理类
 * </p>
 *
 * @author Patrick
 * @since 2024-07-02
 */
@Data
public class JsonResult {

    @Schema(description = "状态码")
    private Integer code;

    @Schema(description = "状态信息")
    private Boolean status;

    @Schema(description = "返回信息")
    private String message;

    @Schema(description = "数据")
    private Map<String, Object> data = new HashMap<>();

    /**
     * 请求成功
     *
     * @return success
     */
    public static JsonResult success() {
        JsonResult result = new JsonResult();
        result.setStatus(true);
        result.setCode(HttpCodeEnum.SUCCESS.getCode());
        result.setMessage(HttpCodeEnum.SUCCESS.getMessage());
        return result;
    }

    /**
     * 请求失败
     *
     * @return error
     */
    public static JsonResult error() {
        JsonResult result = new JsonResult();
        result.setStatus(false);
        result.setCode(HttpCodeEnum.ERROR.getCode());
        result.setMessage(HttpCodeEnum.ERROR.getMessage());
        result.data(null);
        return result;
    }

    /**
     * 设置 状态码 和 信息 (一)
     *
     * @param code 状态码
     * @param message 信息
     * @return JsonResult
     */
    public JsonResult codeAndMessage(Integer code, String message) {
        this.setCode(code);
        this.setMessage(message);
        return this;
    }

    /**
     * 设置 状态码 和 信息 (二)
     *
     * @param httpCodeEnum 枚举信息
     * @return JsonResult
     */
    public JsonResult codeAndMessage(HttpCodeEnum httpCodeEnum) {
        this.setCode(httpCodeEnum.getCode());
        this.setMessage(httpCodeEnum.getMessage());
        return this;
    }

    /**
     * 设置 数据 (一)
     *
     * @param key key
     * @param value value  ==> Object
     * @return JsonResult
     */
    public JsonResult data(String key, Object value) {
        this.data.put(key, value);
        return this;
    }

    /**
     * 设置 数据 (二)
     *
     * @param data Map 集合
     * @return JsonResult
     */
    public JsonResult data(Map<String, Object> data) {
        this.setData(data);
        return this;
    }

}

@Data注解为Lombok工具类库中的注解,提供类的get、set、equals、hashCode、canEqual、toString方法,使用时需配置Lombok,如不配置请手动生成相关方法。

@RestController
@Tag(name = "测试")
@RequestMapping("/test")
public class UserController {

    @GetMapping("test1")
    public JsonResult data() {
        HashMap<String, String> map = new HashMap<>();
        map.put("username", "admin");
        map.put("password", "123456");
        return JsonResult.success().data("list", map);
    }

}
{
    "code": 200,
    "status": true,
    "message": "操作成功!",
    "data": {
        "list": {
            "password": "123456",
            "username": "admin"
        }
    }
}

二、自定义异常的基本步骤

1. 解析异常的使用

1. 异常分类

2. 为什么处理异常

3. 什么时候用异常

  1. 系统自动抛出异常
  2. 程序员手动抛出异常

4. 如何处理异常(两种方法)

  1. try - catch 直接解决异常
  2. throws 向上抛异常

2. 自定义业务异常处理

手动抛出异常的步骤:

  1. 自定义异常类,extends继承Excepion(编译时异常)或者RunTimeException(运行时异常)
  2. 自定义构造方法,无参、带参都可,传入异常信息
  3. 上抛throws异常
/**
 * 业务异常处理类
 */
@Getter
public class BusinessException extends RuntimeException {

    /**
     * 状态码
     */
    private final Integer code;

    /**
     * 报错信息
     */
    private final String message;

    /**
     * 全参构造方法
     *
     * @param code    状态码
     * @param message 报错信息
     */
    public BusinessException(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    /**
     * 构造方法
     *
     * @param message 报错信息
     */
    public BusinessException(String message) {
        this(HttpCodeEnum.ERROR.getCode(), message);
    }

    /**
     * 构造方法
     * @param httpCodeEnum http枚举类
     */
    public BusinessException(HttpCodeEnum httpCodeEnum) {
        this(httpCodeEnum.getCode(), httpCodeEnum.getMessage());
    }

}

3. 自定义全局异常处理

@RestControllerAdvice:是一个 Spring Boot 中用于全局异常处理的注解。它结合了@ControllerAdvice 和@ResponseBody 两个注解的功能,可以用于处理所有控制器中抛出的未处理异常,并返回自定义的响应数据

@ExceptionHandler:是异常处理器,两个结合表示当出现异常的时候执行某个通知

Exception.class:是 Java 中的一个类对象,它表示了 java.lang.Exception 类。Exception 是所有异常类的父类,它包含了一些通用的方法和属性,例如异常信息、异常堆栈跟踪等

/**
 * 全局异常信息处理类
 */
@Slf4j
@RestControllerAdvice
public class GlobalException {

    /**
     * 处理业务异常
     *
     * @param e BusinessException
     * @return businessException
     */
    @ExceptionHandler(value = BusinessException.class)
    public JsonResult businessException(BusinessException e) {
        log.error("业务异常 => code: {}, 原因是: {}", e.getCode(), e.getMessage());
        return JsonResult.error().codeAndMessage(e.getCode(), e.getMessage());
    }

    /**
     * 处理未知(600)异常
     *
     * @param e otherException
     * @return globalException
     */
    @ExceptionHandler(value = Exception.class)
    public JsonResult globalException(Exception e) {
        log.error("未知(600)异常 => 原因是: {}", e.getMessage());
        return JsonResult.error().codeAndMessage(HttpCodeEnum.RC600);
    }

}

需要注意的是一个异常只会被捕获一次,比如空指针异常,只会被第二个方法捕获,处理之后不会再被最后一个方法捕获。当上面两个方法都没有捕获到指定异常时,最后一个方法指定了@ExceptionHandler(value = Exception.class)就可以捕获到所有的异常,相当于if elseif else语句

分别测试自定义异常、空指针异常以及其他异常:

  1. 自定义异常
@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * 测试业务异常
     */
    @PostMapping("/test")
    public void test() {
        int i = 0;
        throw new BusinessException(HttpCodeEnum.RC403);
    }

}
{
    "code": 403,
    "status": false,
    "message": "您没有操作权限!",
    "data": null
}

  1. 空指针异常:
    /**
     * 测试空指针异常
     */
    @PostMapping("/test1")
    public void test1(int id, String name) {
        boolean equals = name.equals("id");
        System.out.println("id: " + id + ", name: " + name);
    }
{
    "code": 400,
    "status": false,
    "message": "参数格式不合法,请检查后重试!",
    "data": null
}

  1. 其他异常:
    /**
     * 测试其他异常
     */
    @PostMapping("/test2")
    public void test2() {
        int i = 1 / 0;
    }
{
    "code": 600,
    "status": false,
    "message": "服务器繁忙或服务器错误,请稍后再试!",
    "data": null
}

4. 处理404与405异常

1. 处理404异常

首先,我们在application.yml配置文件增加以下配置项:

spring:
  #  关闭默认的静态资源路径映射
  web:
    resources:
      add-mappings: false

{
    "code": 600,
    "status": false,
    "message": "服务器繁忙或服务器错误,请稍后再试!",
    "data": null
}

    /**
     * 处理页面不存在(404)异常
     *
     * @param e HttpServletRequest
     * @return noHandlerFoundException
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public JsonResult noHandlerFoundException(HttpServletRequest e) {
        log.error("请求地址错误(404)异常 => 请求方式: {}, 请求地址: {}", e.getMethod(), e.getServletPath());
        return JsonResult.error().codeAndMessage(HttpCodeEnum.RC404);
    }

在上面中,我们使用@ExceptionHandler(value = NoHandlerFoundException.class)单独捕获处理404异常,统一返回格式中code也设置为404。

你也可以使用@ResponseStatus(HttpStatus.NOT_FOUND)注解指定http状态码为404

{
    "code": 404,
    "status": false,
    "message": "未找到您请求的资源!",
    "data": null
}

2. 处理405异常

    /**
     * 处理请求方式错误(405)异常
     *
     * @param e HttpServletRequest
     * @return httpRequestMethodNotSupportedException
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public JsonResult httpRequestMethodNotSupportedException(HttpServletRequest e) {
        log.error("请求方式错误(405)异常 => 请求方式: {}, 请求地址: {}", e.getMethod(), e.getServletPath());
        return JsonResult.error().codeAndMessage(HttpCodeEnum.RC405);
    }

{
    "code": 405,
    "status": false,
    "message": "请求方式错误,请检查后重试!",
    "data": null
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文