这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
前言
- 在正常的开发过程中,因为前端展示的界面多种多样,所以数据会有各种各样的结构,如果后台对数据不加封装的情况下,数据格式就会千奇百怪,最终会降低开发效率,因此需要统一格式.
- 在程序发生异常的情况下, 异常信息提示的格式,很大程度上也和正常返回不一致(和全局异常处理一起阅读更佳),所以需要统一格式
- 在业务进行中,有些接口请求成功,但是因为业务需要,需要返回一些特殊的状态,在统一了数据格式之后,前端可以在请求入口对这些状态统一的处理.所以需要统一格式
实现思路
-
在项目我我们常常封装一个返回对象类,在类中定义好标准的数据格式.
正常情况下,我们会将定义三个参数
code
,msg
,data
.data
用于存防业务数据,code
用来展示统一定义的状态码msg
用来展示一些基础的成功或者失败信息 -
也会创建一个状态码枚举类,类似这样
public enum ResultCode {
SUCCESS(200,"操作成功"),
NO_LOGIN(301,"请重新登录"),
NO_AUTH(302,"您还没有此功能的权限"),
NO_UPDATE_DATA(303,"该数据不能操作"),
NOT_ACCESS(304,"禁止访问"),
FAIL(5000,"操作失败,请联系管理员"),
CUSTOM_FAIL(5001,"自定义错误")
//等等等
;
private int code;
private String msg;
public int code(){
return this.code ;
}
public String msg(){
return this.msg ;
}
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
一般对于中小型公司的中小型项目,正常的状态和异常的状态各个定义一个就好,全靠msg展示信息是没问问题的, 如果有问题的话就去看alibaba出品的java规范.
返回数据的多种实现
(1) Object接收各种数据
public class R implements Serializable {
private int code;
private String msg;
private Object data ;
private R() {}
private R(ResultCode resultCode, String msg) {
this.code = resultCode.code();
this.msg = msg;
}
/***
* 操作成功,当参数为1个,则将数据直接指向data字典, 当 参数为偶数个时,将单数参数设置为Map的key,每个随后的参数设置为Value
*/
public static R ok(Object... data){
R resultVo = new R(200,"操作成功");
resultVo.data =integrationData(data);
return resultVo;
}
/***
* 自定义异常,需要返回异常信息
*/
public static R fail(String failMsg, Object... data){
R resultVo = new R(5000,failMsg);
resultVo.data =integrationData(data);
return resultVo;
}
//.... 只包含基本的,各种变种没有写...
/***
* 将返回参数整理到data中
* @param data
* @return
*/
private static Object integrationData(Object... data){
if(data==null||data.length==0){ return null; }
int length = data.length;
if(length==1){ return data[0]; }
if(length %2 ==1){
throw new RuntimeException("返回结果参数数量不正确,应该为偶数,实际为:"+length);
}
if(length %2 ==0){
HashMap<Object, Object> map = new HashMap<>();
for (int i = 0; i < length; i+=2) {
map.put(data[i],data[i+1]);
}
return map;
}
return "";
}
}
使用方式
R.OK("数据1");
R.OK("数据1","KEY2","数据2");
或许你看到的是这种,这种如果存在多个数据,只能手动创建一个Map然后将数据放到data中
public class R<T> implements Serializable {
private int code;
private String msg;
private T data ;
}
(2) Map接收数据
public class R extends HashMap<String, Object>{
public R() {
put("code", 0);
put("msg", "操作成功");
}
public static R error(String msg) {
return error(5000, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok() {
return new R();
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok(Object data) {
return new R().put("data",data);
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
这种数据格式就是利用了map来做返回对象,可以想map一样操作他
R.OK("数据1");
R.OK("数据1").put("KEY2","数据2");
(3) 实现ResponseBodyAdvice
并注解@RestControllerAdvice
这种方法比较少见,并且在实际使用过程中,如果沟通不畅出现问题的几率是比较大的
@RestControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice<Object> {
//开启支持
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
//处理封装数据
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//处理指定类型的数据
if(o instanceof String){
R.ok(o)
}
//更多的处理方式
return o;
}
}
这个的使用方式就是;在cotroller想怎么写就怎么写,在MyResponseAdvice
会自动封装.
配合全局异常处理需要做的地方
详见指定文章的指定两个部分:
1.拦截404或者服务器错误等未进入controller的异常
作者:ZOUZDC
链接:https://juejin.cn/post/7028963866063306760
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。