SpringBoot统一返回结果

2,037 阅读4分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

后端开发时,在不同的业务场景中会获取并返回不同类型的数据,而数据格式的多样化对于前端同事来讲是一个巨大灾难,这就需要后端接口返回的数据形成一个统一规范的结果类型。前后端开发过程中数据交互规范化是一件非常重要的事情,不仅减少了前后端交互过程中出现的问题,也让代码逻辑更加具有条理。

SpringBoot统一返回结果

封装返回结果类

返回结果类基本特征

对于后端的返回数据,考虑将格式统一后返回,在开发那么多的后端接口之后,我们可以从经验中总结得到,请求一个接口时需要关注的指标有:

  • 响应状态码,即请求接口返回状态码,如HTTP请求中的200、304、500等状态
  • 响应结果描述,有些接口请求成功或失败需要返回描述信息供前端展示
  • 响应结果数据,大部分的接口都会返回后端获取的数据,并以列表的形式展示的前端页面中
  • 是否成功:在实际项目中请求接口时,首先要关注的应该是接口的请求是否成功,然后才会去关注成功返回数据或者错误代码和信息,那么我们也在统一数据中加入请求是否成功的标识。
  • 其他标识:为了显示更多接口调用的信息,可能会根据实际的业务需求加入接口调用的时间信息等。 除了以上属性特征外,返回结果类在定义时还应该满足:
  1. 属性私有化,使用get/set方法来操作属性值
  2. 构造器私有化,外部只可以调用方法,初始化要在类内部完成
  3. 由于外部需要直接调用方法,因此方法要定义为静态方法
  4. 每个方法的返回结果应该时类本身,更好的使用链式编程

松散的自定义返回结果

如下代码所示为我们定义的返回结果类信息:

@Data
public class Result {
    //是否请求成功
    private Boolean success;
    private Integer code;
    private String desc;
    private Object data;
    //请求时间
    //private long timestamp;
    
    //构造器私有
    private Result() { 

    }
    
    /**
     * 返回通用成功
     * @return Result
     */
    public static Result ok(){
        Result result = new Result();
        result.setSuccess(true);
        result.setCode("20000");
        result.setDesc("请求成功");
        return result;
    }

    /**
     * 返回通用失败,未知错误
     * @return Result
     */
    public static Result error(){
        Result result = new Result();
        result.setSuccess(false);
        result.setCode(20001);
        result.setDesc("请求失败");
        return result;
    }
    ...
}

代码中在定义返回结果类时,每个方法中的返回值仍需要手动添加,这样即增加了数据错误的风险,又增加了数据变动时的工作量。因此我们采用返回结果枚举类的方式将所有可能返回的结果定义为枚举类常量,在返回结果类中使用对应的枚举类返回创建。

状态码枚举类

枚举类的使用,进一步规范了返回结果类中定义的属性取值。

@Getter
public enum ResultCodeEnum {

    SUCCESS(true,20000,"响应成功"),
    UNKNOWN_ERROR(false,20001,"未知错误"),
    PARAM_ERROR(false,20002,"参数错误"),
    NULL_POINT_ERROR(false,20003,"空指针异常"),
    HTTP_CLIENT_ERROR(false,20003,"客户端连接异常");

    /**
     * 响应是否成功
     */
    private Boolean success;
    
    /**
     * 响应状态码
     */
    private Integer code;

    /**
     * 响应描述信息
     */
    private String desc;

    ResultCodeEnum(Boolean success, Integer code, String desc){
        this.success = success;
        this.code = code;
        this.desc = desc;
    }
}

枚举类+自定义返回结果类

@Data
public class Result {
    //是否请求成功
    private Boolean success;
    private Integer code;
    private String desc;
    private Object data;
    //请求时间
    //private long timestamp;
    //构造器私有
    private Result() { 

    }
    
    /**
     * 返回通用成功
     * @return Result
     */
    public static Result ok(){
        Result result = new Result();
        result.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        result.setCode(ResultCodeEnum.SUCCESS.getCode());
        result.setDesc(ResultCodeEnum.SUCCESS.getDesc());
        return result;
    }

    /**
     * 返回通用失败,未知错误
     * @return Result
     */
    public static Result error(){
        Result result = new Result();
        result.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess());
        result.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode());
        result.setDesc(ResultCodeEnum.UNKNOWN_ERROR.getDesc());
        return result;
    }

    /**
     * 使用枚举类设置返回结果
     * @param resultCodeEnum
     * @return
     */
    public static Result setResult(ResultCodeEnum resultCodeEnum){
        Result result = new Result();
        result.setSuccess(resultCodeEnum.getSuccess());
        result.setCode(resultCodeEnum.getCode());
        result.setDesc(resultCodeEnum.getDesc());
        return result;
    }
    
    /**
     * 返回结果类,使用链式编程
     * 自定义成功标识
     * @param 
     * @return
     */
     public Result success(Boolen success){
        this.setSuccess(success);
        return this;
    }
    
    /**
     * 返回结果类,使用链式编程
     * 自定义状态码
     * @param 
     * @return
     */
     public Result code(Integer code){
        this.setCode(code);
        return this;
    }
    
    /**
     * 返回结果类,使用链式编程
     * 自定义返回结果描述
     * @param 
     * @return
     */
     public Result desc(String desc){
        this.setDesc(desc);
        return this;
    }
    
    /**
     * 返回结果类,使用链式编程
     * 自定义结果数据
     * @param 
     * @return
     */
     public Result data(Object data){
        this.setData(data);
        return this;
    }
     
}

接口请求

定义好返回结果枚举类和返回结果类后,我们在controller控制器中创建一个接口并返回统一结果类信息,可以得到如下格式的返回结果:

  "success"true,
  "code"20000,
  "desc""查询成功",
  "data": {
    "itms": [
      {
        "id""1",
        "username""admin",
        "role""ADMIN",
      },{
        "id""2",
        "username""zhangsan",
        "role""USER",
      }
    ]
  }
}

这样,一个统一的结果返回类就创建成功了,我们在项目的开发过程中可以使用自定义的统一返回结果,只需要将我们的返回结果枚举类维护起来。