当前位置 博文首页 > m0_46695378的博客:统一响应实体类的设计
最近在看一些开源项目的时候发现封装统一的响应数据的实体类基本上就包括code,msg,data三个属性分别表示状态码,响应信息,响应数据。并且实体类大多都会继承HashMap<String,Object>。我以前的做法是直接写一个实体类,data字段直接用Object类型定义好,所以传给前端的数据都是在data字段值取。现在想想如果继承HashMap后直接用put方法存放数据那么数据的key值就可以自己定义,比较灵活~
关于响应实体类的内容编写这个真的很难去统一,结合实际项目的要求会更好;个人觉得可以写一个枚举类把常见的错误信息和响应码对应起来,比如用JSR303校验数据的时候几乎所有需要CRUD的实体类都要进行数据的校验,校验失败的错误信息最好固化下来,代码如下:
public enum BizCodeEnum {
/**
* 未知异常
*/
UNKNOW_EXCEPTION(10000,"系统未知异常"),
/**
* 业务操作成功
*/
SUCCESS(0,"操作成功"),
/**
* 业务操作异常
*/
ERROR(500,"操作失败"),
/**
* 参数校验异常
*/
VAILD_EXCEPTION(10001,"参数校验失败");
private final Integer code;
private final String msg;
BizCodeEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
其实这种方式也很灵活,如果代码中出现以上错误就可以直接用固化的信息传给msg字段。阿里最新的《JAVA开发手册》也给出了很多错误码对应的错误附录,正好在这里可以对应 。如果需要http相关的错误码可以用HttpStatus(org.apache.http包)接口中的静态常量名来代替直接写错误码;
五花八门的也没有相对准确写法之说,总之也是那三个字段且是静态常量类型哦,如果想把数据字段的名字统一就提前定义好(个人偏向于统一为data,前端拿到的数据就统一了方便前后联调)。成功或者失败的情况。考虑了好几次加上参考开源项目总结的响应实体结构如下:
public class AjaxResult extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private static final String CODE_TAG = "code";
private static final String MSG_TAG = "msg";
private static final String DATA_TAG = "data";
public AjaxResult(){};
public AjaxResult(Integer code,String msg){
super.put(CODE_TAG,code);
super.put(MSG_TAG,msg);
}
public AjaxResult(Integer code,String msg,Object data){
super.put(CODE_TAG,code);
super.put(MSG_TAG,msg);
super.put(DATA_TAG,data);
}
/**
* 只返回成功消息
* @return 响应实体
*/
public static AjaxResult success(){
return AjaxResult.success(BizCodeEnum.SUCCESS.getMsg());
}
/**
* 返回数据
*
* @param data 数据对象
* @return 响应实体
*/
public static AjaxResult success(Object data){
return AjaxResult.success(BizCodeEnum.SUCCESS.getMsg(),data);
}
/** 自定义返回消息
*
* @param msg 返回内容
* @return 响应实体
*/
public static AjaxResult success(String msg){
return AjaxResult.success(msg,null);
}
/**
* 返回自定义消息和数据
*
* @param msg 返回内容
* @param data 数据对象
* @return 响应实体
*/
public static AjaxResult success(String msg,Object data){
return new AjaxResult(BizCodeEnum.SUCCESS.getCode(),msg,data);
}
/**
* 返回默认消息
*
* @return 响应实体
*/
public static AjaxResult error(){
return AjaxResult.error(BizCodeEnum.ERROR.getMsg());
}
/**
* 自定义返回消息
*
* @param msg 返回内容
* @return 响应实体
*/
public static AjaxResult error(String msg){
return AjaxResult.error(msg,null);
}
/**
* 返回自定义消息和数据
*
* @param msg 返回内容
* @param data 数据对象
* @return 响应实体
*/
public static AjaxResult error(String msg,Object data){
return new AjaxResult(BizCodeEnum.ERROR.getCode(),msg,data);
}
}
上述代码结构在日常开发可以直接这样定义,也是我多次考虑修改的结果,大家可以放心参考,后续还可以优化的话会继续来完善。
在各层抛出的异常都应该由统一异常类来做处理,现在都是前后端分离的开发方式了,所以发生错误时给前端响应为JSON错误信息即可。正好结合前面的统一响应实体,Controller、Service、DAO层拦截异常转换为自定义异常,不允许将异常私自截留。必须对外抛出。[为什么要把系统运行时异常捕获转换为自定义异常抛出呢? 因为用户大多情况下不认识这种异常是什么东西,但是转换为自定义异常就要求程序员对运行时异常进行一个翻译,比如:自定义异常里面有msg字段,后端程序员应该明确的在msg字段里面用面向用户的友好语言,说明发生了什么。]
@RestControllerAdvice注解标注的类可以作为全局的异常处理类,业务中所出现的各类运行时异常都可以分好类由它来做处理,以下代码可以参考:
@RestControllerAdvice(basePackages = " xxxxxx.controller")
public class GlobalExceptionController {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public AjaxResult handleValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
List<FieldError> errors = bindingResult.getFieldErrors();
Map<String, String> error =new HashMap<>();
errors.forEach(item-> error.put(item.getField(),item.getDefaultMessage()));
return AjaxResult.error(BizCodeEnum.VAILD_EXCEPTION.getCode(),BizCodeEnum.VAILD_EXCEPTION.getMsg(),error);
}
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
return R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(),BizCodeEnum.UNKNOW_EXCEPTION.getMsg());
}
}
上面代码中我把参数校验异常都统一做了捕获在这里处理,自己的项目中出现的各类异常都可以在这里分门别类的做处理。大家可以参考。
——————————————————————————————————————7.31号更——>
——这里以Vue框架为例,网络请求采用axios;后端返回的data、code字段可用来判断业务成功与否,msg字段用来作页面的提示消息。
——对于所有的业务我们基本上都要判断成功与否并给用户友好提示,如果在每个页面中都写大量冗余重复的消息提示前端代码后来会变得很“膨胀”。对于Vue经常搭配ElementUI一起使用,其中有Message组件专门用作消息提示,并且可以单独在模块中引用:import { Message } from ‘element-ui’;
——
要注意的一点是服务器返回 200 OK 并不代表结果是预期正确的,只表示服务器响应了,还要判断响应结果和预期的是否一致。如常见的5XX错误,是服务器的错误,而不是请求出错。
import axios from 'axios';
import { Message } from 'element-ui';
axios.interceptors.response.use( response => {
if(response.status === 200 && response.data.status === 500){
//当请求成功但实际业务不正确时.......
Message.error({message:response.data.msg});
return;
}
if(response.data.msg){
Message.success({message:response.data.msg});
}
return response.data;
},error => {
return Promise.reject(error)
});
经过上述操作,在业务代码中我们拿到的响应就是实际返回的data字段数据,这样我们只需要再判断data字段是否为空然后进行相应的赋值操作。
cs