问题背景
一个用户的操作,比如登录,就可能会有很多种情况,用户层面的:账号错误、密码错误;服务器层面的:数据库错误等等情况。但是我们正常的是直接把一个对象解析成json对象返回给前端,那我们如何去判断是哪一种情况,并给用户一个反馈信息呢?前端就犯难了。这时候,我们就想出了一个方法,既然都是对象,那我能不能嵌套对象,一个对象包含状态码,提示信息,和返回值呢,这时候统一结果集就孕育而生了。
解决思路
首先我们定义一下我们的对象格式,格式如下所示:
-
code:自定义请求状态码,前端判断的依据
-
msg:用户提示信息,前端就可以直接弹出
-
data:后台真正返回的数据,比如说token、查询列表等。
结果集V1
很简单的一个思路,即我们要返回前端的一般都是一个json对象,那么我们只需要把我们真正需要返回前端的对象,嵌套进另一个对象即可。那么可以写出这样的代码。
package cn.stopyc.constant;
/**
* @program: return-project
* @description: 返回值包装
* @author: stop.yc
* @create: 2022-04-01 12:24
**/
public class Result<T> {
/**
* 编号
*/
private Integer code;
/**
* 信息
*/
private String msg;
/**
* 数据
*/
private T data;
public Result() {
super();
}
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
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 T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
通过泛型的定义,我们可以直接把需要返回前端的对象,返进data里面即可。但是,实践中发现,状态码、响应信息等都是直接硬编码在代码里面,没办法形成统一等风格。那么,我们就可以维护一个枚举类,统一编码,然后前后端也更容易协调。
package cn.stopyc.constant;
/**
* @Description: 结果集枚举
* @Param:
* @return:
* @Author: stop.yc
* @Date: 2022/4/28
*/
public enum ResultEnum {
//自定义
//通用
UNKNOWN_ERROR(-1,"未知错误"),
SUCCESS(200,"成功"),
SERVER_INTERNAL_ERROR(500,"服务器内部错误"),
RESOURCE_NOT_FOUND(404,"资源未找到"),
PARAMETER_NOT_VALID(400,"参数不合法"),
DATABASE_ERROR(600,"数据库操作错误"),
//用户模块 5002XX
PASSWORD_FAILED(500200,"密码错误"),
REPEAT_NAME(500210,"用户名重复"),
FIND_USER_FAILED(500220,"用户名不存在"),
CHECK_CODE_ERROR(500230,"验证码错误"),
//任务模块 6002XX
NO_TASK(600200,"当前没有任务"),
//.......
;
/**
* 编号
*/
private Integer code;
/**
* 信息
*/
private String msg;
ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
经过这样的改进,我们最后controller返回信息的时候,只需要这样书写即可。
return new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), data);
结果集V2
看到第一版的代码,似乎已经很不错了,但是可以看到,最后返回的代码中,出现了两次类似的东西,ResultEnum,而且调用的枚举还是同一个,所以,是不是可以考虑把这两部分抽象一下,然后Result对象内部自己组装code和msg呢?所以,这里我们对Result对象实例化的时候的传参,使用了一个StatusCode接口。代码如下。
//接口
public interface StatusCode {
int getCode();
String getMsg();
}
//结果集类
public class Result {
/**编号*/
private int code;
/**信息*/
private String msg;
/**数据*/
private Object data;
/**
* 无参构造方法(保险)
*/
public Result() {
super();
}
/**
* 手动指定状态码
* @param code :状态码
* @param msg :信息
* @param data :数据
*/
public Result(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 没有返回值,
* @param statusCode :状态
*/
public Result(StatusCode statusCode) {
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
}
/**
* 有返回值
* @param statusCode :状态
* @param data :数据
*/
public Result(StatusCode statusCode,Object data) {
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
this.data = data;
}
public Result(Object data) {
this.code = ResultEnum.SUCCESS.getCode();
this.msg = ResultEnum.SUCCESS.getMsg();
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
//状态枚举
public enum ResultEnum implements StatusCode{
//自定义
//通用
UNKNOWN_ERROR(-1,"未知错误"),
SUCCESS(200,"成功"),
UNAUTHORIZED(401,"请先登录"),
FORBIDDEN(403,"权限不足"),
RESOURCE_NOT_FOUND(404,"资源未找到"),
PARAMETER_NOT_VALID(400,"参数不合法"),
SERVER_INTERNAL_ERROR(500,"服务器正在忙碌中,请稍后试试吧"),
;
/**
* 编号
*/
private int code;
/**
* 信息
*/
private String msg;
ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
//启动类
public class App {
public static void main(String[] args) {
String data = "12345";
//直接输入数据,默认封装请求成功状态码
System.out.println(new Result(12345));
//指定状态码,加数据
System.out.println(new Result(ResultEnum.CODE_ADD_ERR,data));
//指定状态码,没有数据
System.out.println(new Result(ResultEnum.CODE_SELECT_ERR));
int i = 0;
System.out.println(new Result((i == 1 ? ResultEnum.CODE_ADD_OK : ResultEnum.CODE_ADD_ERR)));
}
}
结果集V3
v2的已经挺完美了,但是,在实际的代码书写中,还是会发现一些问题,就是我们频繁的写一个成功的code,也就是200code。那这个能不能变成一个方法进行调用呢?代码如下。
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Result {
/**编号*/
private Integer code;
/**信息*/
private String msg;
/**数据*/
private Object data;
public static Result ok() {
return new Result()
.setCode(ResultEnum.REQUEST_SUCCESS.getCode())
.setMsg(ResultEnum.REQUEST_SUCCESS.getMsg());
}
public static Result ok(Object data) {
return new Result()
.setCode(ResultEnum.REQUEST_SUCCESS.getCode())
.setMsg(ResultEnum.REQUEST_SUCCESS.getMsg())
.setData(data);
}
public static Result fail(StatusCode statusCode) {
return new Result()
.setCode(statusCode.getCode())
.setMsg(statusCode.getMsg());
}
public static Result fail(StatusCode statusCode,Object data) {
return new Result()
.setCode(statusCode.getCode())
.setMsg(statusCode.getMsg())
.setData(data);
}
}
StatusCode接口代码:
public interface StatusCode {
int getCode();
String getMsg();
}
使用实例代码:
return Result.ok();
return Result.fail(PHONE_CODE_ERR);
return Result.ok(data);
return Result.fail(PHONE_CODE_ERR,data);
总结
一些代码,总是会遵循一个先完成后优化的规律。随着业务的发展和代码的实践过程中,总是会发现一些问题,那么,我们就需要好好思考,去进行相应的改进,去把他优化、升级,使其更加的优雅,美观。