我们的项目是前后端分离的架构,那么在前后端分离的架构中,最重要的是接口和响应结果信息的封装。前端和后端通过接口来进行数据传输和交互,所以我们后端所有对外的接口都是Restful接口,以及对前端的响应都是json格式的数据。
1. Restful接口简介
首先我们简单了解下什么是Restful接口,下面我们看下对于简单的增删改查,传统写法和使用Restful接口的区别。
1.1 传统接口设计
接口描述 | 接口地址 | 请求方法 |
---|---|---|
查询 | /user/query?name=tom | GET |
查看详情 | /user/getInfo?id=1 | GET |
创建 | /user/create | POST |
修改 | /user/update?id=1 | POST |
删除 | /user/delete?id=1 | GET |
1.2 Restful接口设计
接口描述 | 接口地址 | 请求方法 |
---|---|---|
查询 | /user?name=tom | GET |
查看详情 | /user/1 | GET |
创建 | /user | POST |
修改 | /user/1 | PUT |
删除 | /user/1 | DELETE |
1.3 两者区别
(1)传统接口设计中使用URL描述行为,Restful接口使用URL描述资源。比如在传统接口地址中有/query
、/getInfo
来表示这是查询和获取详情的含义;而Restful接口地址中看不到任何行为的描述,比如想要查看用户id=1
或者修改用户id=1
的信息,接口地址都是/user/1
,用户id=1对系统来说是一个资源,用URL来描述系统有哪些资源。
(2)Restful接口使用HTTP方法描述行为,使用HTTP状态码来表示不同的结果。比如对于/user/1
这个接口地址,通过HTTP方法GET
、PUT
、DELETE
分别表示查看、修改、删除。
1.4 说明
Restful接口只是一种风格,并不是强制的标准。即不管用传统的接口设计还是用Restful接口设计都是可以的,并没有说传统的接口设计就是错的,或者不好的。
2. 响应结果实体类封装
基于以上描述,在Restful接口设计中使用HTTP状态码来表示不同的结果,所以我们将对不同响应操作码和响应结果实体类进行封装。
2.1 响应操作码接口
在mall-commons-api
中创建com.autumn.mall.commons.response
包,然后在该包下创建ResultCode
接口。
public interface ResultCode {
/**
* 操作是否成功,true为成功,false为失败
*
* @return
*/
boolean isSuccess();
/**
* 操作码
*
* @return
*/
int getCode();
/**
* 提示信息
*
* @return
*/
String getMessage();
}
2.2 封装通用响应操作码
将整个项目中一些常见的报错信息枚举,比如SUCCESS
表示响应成功,FAIL
表示响应失败。
@ToString
@AllArgsConstructor
public enum CommonsResultCode implements ResultCode {
SUCCESS(true, "00000", "操作成功!"),
FAIL(false, "00001", "操作失败!"),
INVALID_PARAM(false, "00002", "非法参数!"),
ENTITY_IS_NOT_EXIST(false, "00003", "指定唯一标识的实体不存在!"),
TRY_LOCKED_ERROR(false, "00004", "获取分布式锁过程中报错!"),
SERVER_ERROR(false, "99999", "抱歉,系统繁忙,请稍后重试!");
private boolean success;
private int code;
private String message;
@Override
public boolean isSuccess() {
return success;
}
@Override
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
每个微服务需要定义个性化的响应操作码实现类,以招商微服务
举例,在mall-investment-api
模块下,我们通过实现ResultCode
接口创建InvestResultCode
枚举类,在该类下定义招商微服务各个模块的操作码,并且有一个约定,每个微服务的操作码通常是以某个数字开始的,比如招商微服务所有的响应操作码都是1xxxx
开始。
@Getter
@ToString
@AllArgsConstructor
public enum InvestResultCode implements ResultCode {
CODE_IS_EXISTS(false, "10001", "代码已存在,禁止操作!"),
CODE_IS_NOT_ALLOW_MODIFY(false, "10002", "代码不允许修改!"),
STORE_IS_NOT_ALLOW_MODIFY(false, "10003", "项目不允许修改!"),
BUILDING_IS_NOT_ALLOW_MODIFY(false, "10004", "楼宇不允许修改!"),
FLOOR_IS_NOT_ALLOW_MODIFY(false, "10005", "楼层不允许修改!"),
POSITION_IS_REPEAT(false, "10006", "同一铺位,合同期不允许交叉!"),
ENTITY_IS_DISABLED(false, "10007", "已停用状态的资料,不允许修改!"),
ENTITY_IS_EQUALS_TARGET_STATE(false, "10008", "资料已经是目标状态,禁止重复操作!"),
CONTRACT_IS_EFFECT(false, "10009", "合同是生效状态,不允许删除!");
private boolean success;
private String code;
private String message;
}
2.3 封装响应结果类
服务端返回给前端的数据应该包括响应操作码,还包括响应的接口数据。
@Data
@ToString
public class ResponseResult<T> {
private boolean success;
private String code;
private String message;
private T data;
public ResponseResult() {
}
public ResponseResult(ResultCode resultCode) {
this(resultCode, null);
}
public ResponseResult(ResultCode resultCode, T data) {
this.success = resultCode.isSuccess();
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.data = data;
}
/**
* 操作成功
*
* @return
*/
public static ResponseResult SUCCESS() {
return new ResponseResult(CommonsResultCode.SUCCESS);
}
/**
* 操作失败
*
* @return
*/
public static ResponseResult FALL() {
return new ResponseResult(CommonsResultCode.FAIL);
}
}