SpringBoot+Vue项目系列教程-05-如何优雅地定义统一返回值

前言

在一个后台中会有很多的接口,所有接口返回统一的结果无疑是非常重要。返回结果中无非包括两个方面的信息,状态(是否成功)和数据,状态是必须的,数据不是必须的。如登录接口,返回结果中只有是否登录成功;获取列表信息的接口,则包括是否成功和成功的对应数据列表。

后端返回给前端的统一结果一般用json定义,

 {
     //状态码
     code:200,
     //与状态码对应的描述信息
     msg:"成功",
     //携带的数据,可为空
     data:{}
 }

如何定义这个结果呢?我们从设计的角度来讲,然后一步一步优化。

将结果封装成对象

将返回值封装成对象,然后在Controller中创建这个对象并返回。

 @Data
 public class Result{
     private Integer code;
     private String msg;
     private Object data;
 }

然后在Controller中

 package com.hgt.controller;
 
 import com.hgt.pojo.Result;
 import com.hgt.pojo.ResultCode;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping(path = "api")
 public class HelloController {
     @RequestMapping(path = "result")
     public Result testResult(){
         return new Result(1,"成功",null);
     }
 }
 

返回值为:{"code":1,"msg":"成功","data":null}

这种方式也能用,但是缺点也很明显:

  1. code与msg可随意定义,没有统一标准,出问题后无法查询
  2. 接口应只专注于业务,每个接口都创建一个Result对象显然不合适

解决第一个问题

我们看一下HTTP请求常见的状态码:

 200 - 请求成功
 301 - 资源(网页等)被永久转移到其它URL
 404 - 请求的资源(网页等)不存在
 500 - 内部服务器错误

分类分类描述1**信息,服务器收到请求,需要请求者继续执行操作2**成功,操作被成功接收并处理3**重定向,需要进一步的操作以完成请求4**客户端错误,请求包含语法错误或无法完成请求5**服务器错误,服务器在处理请求的过程中发生了错误

接口返回的状态其实和上面很相似,也是固定的、有限的若干个。

既然是固定的,可以使用枚举类型定义返回值状态

 package com.hgt.pojo;
 
 //返回结果的code&message
 public enum ResultCode {
     // 成功
     SUCCESS(1, "成功"),
     FAIL(500,"异常"),
     // 参数无效
     PARAM_IS_INVALID(1001, "无效参数"),
     PARAM_IS_BLANK(1002, "参数为空"),
     PARAM_IS_TYPE_ERROR(1003, "参数类型错误"),
     NO_PERMISSION(1004,"无权访问"),
     PARAM_MISSING(1005,"缺少参数");
 
 
     private Integer code;
     private String msg;
 
     ResultCode(Integer code, String msg) {
         this.code = code;
         this.msg = msg;
     }
 
     public Integer getCode() {
         return this.code;
     }
 
     public String getMsg() {
         return this.msg;
     }
 }

然后将ResultCode与返回数据重新封装成统一返回对象

 package com.hgt.pojo;
 
 import java.io.Serializable;
 
 /**
  * 统一返回结果的封装
  */
 public class Result implements Serializable {
     /*状态码*/
     private Integer code;
     /*状态对应的信息*/
     private String msg;
     /*返回值中的数据,可为空*/
     private Object data;
 
     /**
      * 含数据的返回结果
      *
      * @param resultCode 状态
      * @param data       数据
      */
     public Result(ResultCode resultCode, Object data) {
         this.code = resultCode.getCode();
         this.msg = resultCode.getMsg();
         this.data = data;
     }
 
     /**
      * 无数据的返回结果
      *
      * @param resultCode 状态
      */
     public Result(ResultCode resultCode) {
         this.code = resultCode.getCode();
         this.msg = resultCode.getMsg();
     }
 
     public Integer getCode() {
         return code;
     }
 
     public void setCode(Integer code) {
         this.code = code;
     }
 
     public String getMsg() {
         return msg;
     }
 
     public void setMsg(String msg) {
         this.msg = msg;
     }
 
     public Object getData() {
         return data;
     }
 
     public void setData(Object data) {
         this.data = data;
     }
 
     @Override
     public String toString() {
         return "Result{" +
                 "code=" + code +
                 ", msg='" + msg + '\'' +
                 ", data=" + data +
                 '}';
     }
 }
 

课后作业1:类中构造方法使用ResultCode而不使用code、msg想必大家都能理解吧?如果不理解的可以留言。

控制类对应接口修改为

 @RequestMapping(path = "result")
 public Result testResult() {
         // 无数据的返回值
 //        return new Result(ResultCode.SUCCESS);
         // 有数据的返回值
         return new Result(ResultCode.SUCCESS,"字符串代替数据");
 }

无数据的返回值:

 {"code":1,"msg":"成功","data":null}

有数据的返回值:

 {"code":1,"msg":"成功","data":"字符串代替数据"}

这样优化之后就解决了第一个问题。

在实际开发中,返回值的状态只能由项目经理根据需要往ResultCode中添加,写接口的人员只能使用;前端人员想查看所有文档,只需要查看状态的文档就可以了,这样很优雅地就解决了混乱的问题。

细心的小伙伴可能会发现,控制器返回时,都通过new关键字创建对象,这样显示很low,也很麻烦,下面我们进一步优化返回对象。

静态方法返回结果对象

分成两步:

  1. 构造方法改在private
  2. 创建静态方法,在静态方法中调用构造方法创建对象

具体代码为:

 package com.hgt.pojo;
 
 import java.io.Serializable;
 
 /**
  * 统一返回结果的封装
  */
 public class Result implements Serializable {
     /*状态码*/
     private Integer code;
     /*状态对应的信息*/
     private String msg;
     /*返回值中的数据,可为空*/
     private Object data;
 
     /**
      * 含数据的返回结果
      *
      * @param resultCode 状态
      * @param data       数据
      */
     private Result(ResultCode resultCode, Object data) {
         this.code = resultCode.getCode();
         this.msg = resultCode.getMsg();
         this.data = data;
     }
 
     /**
      * 无数据的返回结果
      *
      * @param resultCode 状态
      */
     private Result(ResultCode resultCode) {
         this.code = resultCode.getCode();
         this.msg = resultCode.getMsg();
     }
 
     /**
      * 状态成功,且无返回数据的返回值
      * @return
      */
     public static Result success() {
         return new Result(ResultCode.SUCCESS);
     }
 
     /**
      * 状态成功,且有返回数据的返回值
      * @param data 数据
      * @return
      */
     public static Result success(Object data) {
         return new Result(ResultCode.SUCCESS,data);
     }
 
     /**
      * 状态失败,且无数据的返回值
      * @param resultCode 返回的状态
      * @return 返回值
      */
     public static Result fail(ResultCode resultCode){
         return new Result(resultCode);
     }
 
     /**
      * 状态失败,且有数据的返回值
      * @param resultCode 不同的状态
      * @param data 数据
      * @return 返回值
      */
     public static Result fail(ResultCode resultCode,Object data) {
         return new Result(resultCode,data);
     }
 
     public Integer getCode() {
         return code;
     }
 
     public void setCode(Integer code) {
         this.code = code;
     }
 
     public String getMsg() {
         return msg;
     }
 
     public void setMsg(String msg) {
         this.msg = msg;
     }
 
     public Object getData() {
         return data;
     }
 
     public void setData(Object data) {
         this.data = data;
     }
 
     @Override
     public String toString() {
         return "Result{" +
                 "code=" + code +
                 ", msg='" + msg + '\'' +
                 ", data=" + data +
                 '}';
     }
 }

控制器中的代码为:

 @RequestMapping(path = "result")
 public Result testResult() {
     // 无数据的返回值
     return Result.success("成功的数据列表");
     // 有数据的返回值
     // return Result.success("字符串代替数据");
 }

返回的数据与上同,但静态调用的方式显然比每次都new一个对象优雅的多。

但目前还不是最优雅的。我们在每个接口中创建的Result对象其它与业务并无关系,只要为了统一的返回值才这样做的。

能否让接口只专注于业务的处理,无论接口返回值是什么对象,都在其它地方能处理成统一的返回格式的呢?这就是要解决的第二个问题。

思路是这样的:

  1. 增加一个自定义注解类,表示接口返回值都要统一包装
  2. 在控制器类或方法中使用此注解
  3. 定义拦截器,使用拦截器对所有请求对所有请求拦截,将所有使用@ResponseResult注解的类和方法做统一处理
  4. 注册拦截器,拦截所有请求
  5. 重新封装返回体

这一部分内容比较重要,也比较难,涉及到自定义注解、拦截器等知识点,所在放在下一节单独讲,代码同步更新。我在网上查询了一下,还没有哪个博客写的这么详细,详细到直接可以用到自己的项目当中。而且这部分功能是SpringBoot项目标配的功能,适用于所有的SpringBoot项目,期待吧!!!

项目代码:https://github.com/316620938/shop-springboot.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值