目录
前言
在之前的文章中我们有介绍过,如何更好、更简单的写好一个接口《接口返回值》,今天的这篇文章我们主要介绍,怎么统一处理下接口的返回格式问题。
问题分析
我们先来分析下我们所面临的问题在哪里,然后接着给出解决方案。在写一个接口时,我们通常会先统一定义一下接口的返回格式是什么,然后在跟前端去对接,通常的返回格式大体两种(我们以保存用户为例):
1.成功/失败响应格式不一致(此种方式作为我们默认的接口响应方式)
保存用户成功,响应体:
{
"id": 10000,
"pwd": "123123",
"nickname": "nickname1",
"img": "http://xxx.com/1.png",
"status": "NORMAL",
"createTime": 1517762718278
}
失败响应体(下面的格式是 spring boot 默认的错误响应格式,只不过我们在其基础上增加了一个 code 字段用于解释更详细的错误码)
{
"status": 400,
"error": "Bad Request",
"message": "参数无效",
"code": 10001,
"path": "/user",
"exception": "org.springframework.web.bind.MethodArgumentNotValidException",
"errors": [
{
"fieldName": "status",
"message": "值是无效的"
}
],
"timestamp": 1515076067369
}
2.成功/失败响应体格式一致
保存用户成功,响应体:
{
"code": 1,
"msg": "成功",
"data": {
"id": 10000,
"pwd": "123123",
"nickname": "nickname1",
"img": "http://xxx.com/1.png",
"status": "NORMAL",
"createTime": 1515076287882
}
}
失败响应体:
{
"code": 10001,
"msg": "参数无效",
"data": [
{
"fieldName": "status",
"message": "值是无效的"
}
]
}
那么如果我们想要的响应体格式是第二种,我们该如何写我们的代码呢?你可能想是这样么?
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public PlatformResult addUser(@Validated @RequestBody User user) {
user.setId("10000");
user.setCreateTime(new Date());
return PlatformResult.success(user);
}
}
PlatformResult.success() 这段逻辑显然很多余,每个方法都要这样写一遍,所以上述方式并不是我们想要的,我们要的是
@ResponseResult(PlatformResult.class)
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User addUser(@Validated @RequestBody User user) {
user.setId("10000");
user.setCreateTime(new Date());
return user;
}
}
我们加了一个自定义的注解 @ResponseResult(PlatformResult.class),参数 PlatformResult.class 告诉这个 Controller 类下的所有方法都以这个类 PlatformResult 的格式进行返回,这个注解可以标记在类或方法上,好了,我们的目的明朗了许多,要做的就是标记这个注解让它实现接口返回值格式控制这个功能,下面我们给出具体的实现方式。
实现思路
首先介绍下完成我们这次主要功能的几个类:
Result 是返回格式类的父接口(所有返回格式类都需要继承它)
PlatformResult 通用返回结果格式(我们上面说的第二种返回结果)
DefaultErrorResult 全局错误返回结果(我们上面说的第一种错误时的返回结果)
GlobalExceptionHandler 全局异常处理
ResponseResult 注解类(用于在Controller上指定返回值格式类)
ResponseResultInterceptor 拦截器(主要用于将 ResponseResult 注解类的标记信息传入 ResponseResultHandler 中)
ResponseResultHandler 响应体格式处理器(主要转换逻辑都在这里)
代码实现
下面将有一大片代码袭来,要顶住!哈哈~
Result 接口类
package cn.notemi.demo.result;
import java.io.Serializable;
/**
* @desc 响应格式父接口
*/
public interface Result extends Serializable {
}
说明
理论上所有的返回格式类都需要实现该接口才能被使用
PlatformResult 通用返回结果
package cn.notemi.demo.result;
import cn.notemi.demo.enums.ResultCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @desc 平台通用返回结果
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class PlatformResult implements Result {
private static final long serialVersionUID = 874200365941306385L;
private Integer code;
private String msg;
private Object data;
public static PlatformResult success() {
PlatformResult result = new PlatformResult();
result.setResultCode(ResultCode.SUCCESS);
return result;
}
public static PlatformResult success(Object data) {
P