目录
数据封装协议
为什么要进行数据封装
实现数据封装
测试
异常处理器
实现异常处理器
项目异常处理
实现处理不同的异常
数据封装协议
为什么要进行数据封装
当接口响应格式不一致时:
前端需要为不同接口编写多种解析逻辑
错误处理逻辑难以统一
接口文档维护成本增加
客户端难以快速判断请求结果
那如何进行数据封装?
前端接收数据格式——创建模型类,封装数据到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 {@Autowiredprivate UserService userService;@PostMappingpublic Result save(@RequestBody User user) {boolean flag = userService.save(user);return new Result(flag?Code.SAVE_OK:Code.SAVE_ERR,flag);}@PutMappingpublic 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);}@GetMappingpublic 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());} }