目录
数据封装协议
为什么要进行数据封装
当接口响应格式不一致时:
前端需要为不同接口编写多种解析逻辑
错误处理逻辑难以统一
接口文档维护成本增加
客户端难以快速判断请求结果
那如何进行数据封装?
前端接收数据格式——创建模型类,封装数据到data属性中、封装特殊消息到message中、封装操作结果到code中。
code:标识状态码
- 告知客户请求处理的结果(成功/失败/异常)
- 通过编码快速定位问题类型(参数错误、权限不足、系统异常)
- 程序化处理:便于客户端编写统一的状态处理逻辑(根据code跳转页面或弹窗)
设计规范:
状态码范围 典型示例 适用场景 2XX 200 业务成功 4XX 400 客户端错误 5XX 500 服务端错误 自定义业务码 10011 具体的业务错误
msg:状态描述信息
可读提示:给开发者或用户展示友好提示信息
问题定位辅助:配合 code 提供具体错误描述
动态信息传递:支持参数化消息(如"用户 {userId} 不存在"
data:业务数据载体
响应主体:承载接口实际返回的业务数据
结构统一:保证不同接口的数据结构一致性
空值处理:无数据时返回
null
或空对象({}
/[]
)
根据上面的原则就可以设置同一数据返回结果类:
public class Result { private Object data; private Integer code; private String msg; }可以根据需要自行增减,提供方法方便操作。
实现数据封装
在controller包下面新建Result类,添加属性code、msg、data,加上对应的set和get方法,以及根据需求写出对应的构造方法:
public class Result { private Object data; private Integer code; private String msg; public Object getData() { return data; } public void setData(Object data) { this.data = data; } 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 Result(Integer code, Object data, String msg) { this.data = data; this.code = code; this.msg = msg; }public Result( Integer code,Object data) { //先码值再数据 this.data = data; this.code = code; } public Result() { } }
接下类设置状态码类:
同样是在contorller包下,新建code包:
public class Code { //1表示成功、0表示失败 public static final Integer SAVE_OK = 20011; public static final Integer DELETE_OK = 20021; public static final Integer UPDATE_OK = 20031; public static final Integer GET_OK = 20041; public static final Integer SAVE_ERR = 20010; public static final Integer DELETE_ERR = 20020; public static final Integer UPDATE_ERR = 20030; public static final Integer GET_ERR = 20040; }
这两项完成后,开始改变之前写的控制类:
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public Result save(@RequestBody User user) {
boolean flag = userService.save(user);
return new Result(flag?Code.SAVE_OK:Code.SAVE_ERR,flag);
}
@PutMapping
public Result update(@RequestBody User user) {
boolean flag = userService.update(user);
return new Result(flag?Code.UPDATE_OK:Code.UPDATE_ERR,flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = userService.delete(id);
return new Result(flag?Code.DELETE_OK:Code.DELETE_ERR,flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
User user = userService.getById(id);
Integer code = user != null ? Code.GET_OK : Code.GET_ERR;
String msg = user != null ? "" : "数据查询失败";
return new Result(code,user,msg);
}
@GetMapping
public Result getAll() {
List<User> users = userService.getAll();
Integer code = users != null ? Code.GET_OK : Code.GET_ERR;
String msg = users != null ? "" : "数据查询失败";
return new Result(code,users,msg);
}
}
和以前的代码对比:
测试
新增测试:
查询单个
异常处理器
在程序开发中,难免会出现异常情况:
通常要根据异常现象的常见位置与诱因来处理。
常见的异常位置有:
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致(服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(索引异常)
- 表现层抛出的异常:因数据收集、校验等规则导致(不匹配的数据类型间接导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(必要释放的连接长期未释放)
SpringMVC提供了快速处理这些异常的方法,就是异常处理器。
实现异常处理器
异常处理器就是集中,统一的处理项目中的异常。
@RestControllerAdvice是Spring框架中的一个注解,该类中可以定义多个方法来处理不同类型的异常。这些方法使用@ExceptionHandler注解来指定要处理的异常类型。当控制器中抛出异常时,Spring会根据异常的类型调用相应的异常处理方法,将异常信息以RESTful的方式返回给客户端。
异常处理器写在控制层,当出现异常时就会被拦截:
@RestControllerAdvice //声明这个类用于异常处理
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class) //拦截Exception异常
public Result deException(Exception exception){
System.out.println("拦截异常...");
return new Result(666,null);
}
}
先写一个错误在查询所有的请求中,然后运行,发送请求:
项目异常处理
项目异常分类
业务异常
- 规范用户行为产生的异常
- 不规范用户行为操作产生的异常
系统异常
- 项目运行过程中可预计且无法避免的异常
其他异常
- 编程人员为预期到的异常
项目异常处理方案
业务异常
- 发送对应消息传递给用户,提醒规范操作
系统异常
- 发送固定信息传递给用户,安抚用户
- 发送特定信息给运维人员,提醒维护
- 记录日志
其他异常
- 发送固定信息传递给用户,安抚用户
- 发送特定信息给编程人员,提醒维护
- 记录日志
实现处理不同的异常
- 创建exception包
- 新建分别用于处理系统异常和业务异常类
//继承RuntimeException,可以出现异常不处理向上抛
//系统异常
public class SystemException extends RuntimeException {
private Integer code; //帮助识别是哪种异常
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
//业务异常
public class BusinessException extends RuntimeException {
private Integer code; //帮助识别是哪种异常
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code,String message, Throwable cause ) {
super(message, cause);
this.code = code;
}
public BusinessException(Integer code,String message ) {
super(message);
this.code = code;
}
}
回到异常处理器,对不同的异常分别进行处理:
@RestControllerAdvice //声明这个类用于异常处理 public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) //拦截Exception异常 public Result deException(Exception exception){ //记录日志 //发送消息到运维 //发送消息给开发人员 System.out.println("拦截异常..."); return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙请稍后再试"); } //拦截到系统异常 @ExceptionHandler(SystemException.class) public Result deSystemException(SystemException exception){ //记录日志 //发送消息到运维 //发送消息给开发人员 return new Result(exception.getCode(),exception.getMessage()); } //处理业务异常 @ExceptionHandler(BusinessException.class) public Result deBusinessException(BusinessException exception){ return new Result(exception.getCode(),exception.getMessage()); } }