【8】SpringBoot统一接口返回的标准格式R.java

  • Post author:
  • Post category:java




SpringBoot统一接口返回的标准格式R.java



01、分析

在后续我们会项目实战,项目实战一种前后端分离的开发方式。我们会通过swagger来进行接口测试。这个时候就会出现一个问题?

一个小团队:1~11人

大的开发团队:20+

  • 每个开发者,对自己的代码都有一套自己的逻辑和哲学,返回值就千变万化。
  • 带来的问题很明显,接口的调用者,就开始尝试一个错觉或者不明确的想象。


怎么办?

统一返回处理


格式如下

# 成功的状态
{
   code:200,
   data:{id:"1",name:"yykkk"},
   message:"success"
}

# 失败
{
   code:401,
   data:"",
   message:"用户名和账号有误"
}

{
   code:500,
   data:"",
   message:"服务器出错!!"
}



02、封装统一返回的R类-基础封装认识

在企业中,有些公司命名:R.java、Result.java 、 ServerResponse 等。其实都是一个意思。

封装如下:

import lombok.Data;

/**
 * # 成功的状态
 * {
 *    code:200,
 *    data:{id:"1",name:"yykkk"},
 *    message:"success"
 * }
*/

@Data
public class R {

    // 返回的编号
    private Integer code;
    // 返回的数据,数据类型N中,
    private Object data;
    // 返回的信息
    private String message;

}

使用如下:

@RestController
@Slf4j
public class RegController {


    @Autowired
    private RegService regService;

    /**
     * @Author xuke
     * @Description 用户注册
     * @Date 20:11 2021/6/23
     * @Param []
     * @return java.lang.String
    **/
    @GetMapping("/reg")
    public R reguser(){
        // 1: 注册用户 10ms
        log.info("新用户注册");
        //userService.save(user);

        // 2: 发送短信 5s
        log.info("发送短信");
        regService.sendMsg();

        // 3: 添加积分 5s
        log.info("添加积分");
        regService.addScore();


        R r = new R();
        r.setCode(200);
        r.setData("ok");
        r.setMessage("注册成功!");

        return r;
    }
}

启动

访问:http://localhost:9999/reg,结果如下:

{"code":200,"data":"ok","message":"注册成功!"}

问题:


频繁的创建R类,是不是有点不妥呢,第一会增加:内存开销,第二:代码臃肿和冗余。



03、封装统一返回的R类-静态方法封装


优化思考:

用静态方法方法去优化和封装,


开始优化


记住:在开发中成功只有一种情况,失败有N中情况。所有成功封装的逻辑是如下:

/**
 * # 成功的状态
 * {
 * code:200,
 * data:{id:"1",name:"yykkk"},
 * message:"success"
 * }
 **/

@Data
public class R {

    // 返回的编号
    private Integer code;
    // 返回的数据,数据类型N中,
    private Object data;
    // 返回的信息
    private String message;

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data, String message) {
        R r = new R();
        r.setCode(200);
        r.setData(data);
        r.setMessage(message);
        return r;
    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data) {
        return success(data, "");
    }
}


启动

访问:http://localhost:9999/reg,结果如下:

{"code":200,"data":"ok","message":"注册成功!"}

解决的问题:


代码臃肿和冗余。



04、封装统一返回的R类-失败的封装


记住:在开发中成功只有一种情况,失败有N中情况。所有成功封装的逻辑是如下:

/**
 * # 失败
 * {
 * code:401,
 * data:"",
 * message:"用户名和账号有误"
 * }
 **/


@Data
public class R {

    // 返回的编号
    private Integer code;
    // 返回的数据,数据类型N中,
    private Object data;
    // 返回的信息
    private String message;

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data, String message) {
        R r = new R();
        r.setCode(200);
        r.setData(data);
        r.setMessage(message);
        return r;
    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data) {
        return success(data, "");
    }


    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description
     * @Date 22:03 2021/6/23
     * @Param [code 失败的状态, message 失败的原因]
     **/
    public static R fail(Integer code, String message) {
        R r = new R();
        r.setCode(code);
        r.setData(null);
        r.setMessage(message);
        return r;
    }
}


测试如下:

@GetMapping("/reg2")
public R reguser2(Integer flag) {
    if (flag.equals(1)) {
        return R.fail(401, "用户名和密码错误!!!");
    }
    if (flag.equals(2)) {
        return R.fail(402, "密码和确认密码不一致!!!");
    }
    // 1: 注册用户 10ms
    log.info("新用户注册");
    //userService.save(user);

    // 2: 发送短信 5s
    log.info("发送短信");
    regService.sendMsg();

    // 3: 添加积分 5s
    log.info("添加积分");
    regService.addScore();

    return R.success("ok");
}

测试结果:

http://localhost:9999/reg2?flag=0 正常返回

{"code":200,"data":"ok","message":""}

http://localhost:9999/reg2?flag=1 失败返回

{"code":401,"data":null,"message":"用户名和密码错误!!!"}

http://localhost:9999/reg2?flag=2 正常返回

{"code":402,"data":null,"message":"密码和确认密码不一致!!!"}



05、封装统一返回的R类-把构造函数私有化


为什么要要构造函数私有化呢?

答案:当调用的过程变得单一,只允许用用类去调用方法,不允许用new去调用。

@Data
public class R {

    // 返回的编号
    private Integer code;
    // 返回的数据,数据类型N中,
    private Object data;
    // 返回的信息
    private String message;

    private R (){

    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data, String message) {
        R r = new R();
        r.setCode(200);
        r.setData(data);
        r.setMessage(message);
        return r;
    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data) {
        return success(data, "");
    }


    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description
     * @Date 22:03 2021/6/23
     * @Param [code 失败的状态, message 失败的原因]
     **/
    public static R fail(Integer code, String message) {
        R r = new R();
        r.setCode(code);
        r.setData(null);
        r.setMessage(message);
        return r;
    }
}



06、封装统一返回的R类-统一返回状态的维护和message的维护问题

如下代码,如果在实际的开发和生产中,这样的编写的话,肯定是会技术leader骂死的,一般早期可以为了完成任务或者业务,可能这样写是没问题。但是随着团队和业务越来越复杂。下面这个就一定要进行统一处理。(

集中管理

if (flag.equals(1)) {
  return R.fail(401, "用户名和密码错误!!!");
}
if (flag.equals(2)) {
  return R.fail(402, "密码和确认密码不一致!!!");
}


集中管理的解决方案

1:用接口或者常量类:比如

public class RConstants {

    // 用户名和密码错误信息和状态
    public static final Integer USER_REG_USER_PASSWORD_CODE = 401;
    public static final String USER_REG_USER_PASSWORD_ERROR = "用户名和密码错误!";

    // 密码和确认密码错误信息和状态
    public static final Integer USER_REG_USER_PASSWORD_CONFIRM_CODE = 402;
    public static final String USER_REG_USER_PASSWORD_CONFIRM_ERROR = "密码和确认密码不一致!";

}
if (flag.equals(1)) {
    return R.fail(RConstants.USER_REG_USER_PASSWORD_CODE,RConstants.USER_REG_USER_PASSWORD_ERROR);
}
if (flag.equals(2)) {
    return R.fail(RConstants.USER_REG_USER_PASSWORD_CONFIRM_CODE,RConstants.USER_REG_USER_PASSWORD_CONFIRM_ERROR);
}

通过常量类:其实并不明确,当我们的错误状态和信息越来越多的时候,这个它的可维护就出现很多问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KLiweuFl-1653404158205)(assets/1624457916519.png)]

2:使用枚举

/**
 * @description: 统一返回的常量类
 * 对内修改开放,对外修改关闭---枚举
 * @author: xuke
 * @time: 2021/6/23 22:12
 */
public enum ResponseEnum {

    USER_REG_USER_PASSWORD_CODE(401,"用户名和密码错误"),
    USER_REG_USER_PASSWORD_CONFIRM(402,"密码和确认密码不一致");

    private Integer code;
    private String message;

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

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

修改如下:

if (flag.equals(1)) {
  return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CODE.getCode(),
                ResponseEnum.USER_REG_USER_PASSWORD_CODE.getMessage());
}
if (flag.equals(2)) {
  return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CONFIRM.getCode(),
                ResponseEnum.USER_REG_USER_PASSWORD_CONFIRM.getMessage());
}



07、封装统一返回的R类-统一枚举的最终方案

@Data
public class R {

    // 返回的编号
    private Integer code;
    // 返回的数据,数据类型N中,
    private Object data;
    // 返回的信息
    private String message;

    private R() {

    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data, String message) {
        R r = new R();
        r.setCode(ResponseEnum.SUCCESS.getCode());
        r.setData(data);
        r.setMessage(message == null ? ResponseEnum.SUCCESS.getMessage() : message);
        return r;
    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description 成功返回
     * @Date 21:55 2021/6/23
     * @Param []
     **/
    public static R success(Object data) {
        return success(data, null);
    }


    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description
     * @Date 22:03 2021/6/23
     * @Param [code 失败的状态, message 失败的原因]
     **/
    public static R fail(Integer code, String message) {
        R r = new R();
        r.setCode(code);
        r.setData(null);
        r.setMessage(message);
        return r;
    }

    /**
     * @return com.kuangstudy.common.R
     * @Author xuke
     * @Description
     * @Date 22:03 2021/6/23
     * @Param [code 失败的状态, message 失败的原因]
     **/
    public static R fail(ResponseEnum responseEnum) {
        R r = new R();
        r.setCode(responseEnum.getCode());
        r.setData(null);
        r.setMessage(responseEnum.getMessage());
        return r;
    }
}


传递枚举引用

package com.kuangstudy.common;

/**
 * @description: 统一返回的常量类
 * 对内修改开放,对外修改关闭---枚举
 * @author: xuke
 * @time: 2021/6/23 22:12
 */
public enum ResponseEnum {

    SUCCESS(200,"成功!"),

    USER_REG_USER_PASSWORD_CODE(401,"用户名和密码错误"),
    USER_REG_USER_PASSWORD_CONFIRM(402,"密码和确认密码不一致"),
    ORDER_FAIL(601,"订单失败"),
    ORDER_MESSAGE_FAIL(602,"订单发送消息失败") ;

    private Integer code;
    private String message;

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

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

调用


    /**
     * @return java.lang.String
     * @Author xuke
     * @Description 用户注册
     * @Date 20:11 2021/6/23
     * @Param []
     **/
    @GetMapping("/reg2")
    public R reguser2(Integer flag) {
        if (flag.equals(1)) {
            return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CODE);
        }
        if (flag.equals(2)) {
            return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CONFIRM);
        }
        // 1: 注册用户 10ms
        log.info("新用户注册");
        //userService.save(user);

        // 2: 发送短信 5s
        log.info("发送短信");
        regService.sendMsg();

        // 3: 添加积分 5s
        log.info("添加积分");
        regService.addScore();

        return R.success("ok");
    }



08、统一返回的参考来源

参考:

1、mybatis-plus–R类

2、springmvc中有一个:ResponseEntity和HttpStatus一个枚举类,如下:

@GetMapping("/reg2")
public ResponseEntity reguser2(Integer flag) {
    // 1: 注册用户 10ms
    log.info("新用户注册");
    //userService.save(user);

    // 2: 发送短信 5s
    log.info("发送短信");
    regService.sendMsg();

    // 3: 添加积分 5s
    log.info("添加积分");
    regService.addScore();
    // 这是失败的
    //return ResponseEntity.status(HttpStatus.BAD_GATEWAY);
    // 这是成功
    return ResponseEntity.ok("success");
}

为什么在开发中,我们不使用springMvc提供好的,还多此一举自己的去定义和实现一个R和一个枚举呢?

答案:

  • 因为springmvc这些满足不了我们的业务开发需求

  • 还有里面的状态的控制和返回,都和业务可能没有什么太大匹配关系。不明确