在进行 web开发 中,如果我们的返回数据不统一,会是啥样呢,比如像下面这种:
java
@RestController
public class DemoController {
@GetMapping("/haha")
public Object haha() {
return "";
}
@GetMapping("/heihei")
public Object heihei() {
Map<String, Object> data = new HashMap<>();
data.put("one", 1);
return data;
}
}
如果返回数据格式不统一,对前后端的数据交互不太友好。另外为了代码更加规范、好维护,我们通常都采取统一的响应体结构。
通常来说不可缺少的三要素:
- code,响应码,标识不同响应
- message,响应信息,作为描述性的
- data,如果有传输的数据,将数据放在data中
有的人会说了,code响应码不是有 httpStatusCode 吗,为何还要指定自己的 code 的呢?其实,这里主要是为了更好地区分不同的响应数据,比如同样的 200 状态码,只要后台正常运行,你能知道到底啥问题吗,但是如果我们限定 0表示成功,非0表示失败,甚至更加具体的如传输文件过大,字段不合规,数据库连接超时等,同样 状态码都是 200 就很难知道到底是什么问题。
为了更加统一,我们通过 枚举 的方式限定 成功、失败 的具体 code和msg,然后在 统一的响应体的规定结构,再写几个不同参数的响应方法,基本就够用了,如果有新的错误等,我们也可以在 枚举 中再添加,大神请勿喷。
接下来我们按照 枚举和响应体类及示例的顺序,演示规范处理。
为了减少代码的书写,我们引入 lombok,依赖如下:
xml
<!-- Lombok 工具 -->
<!-- @Data && slf4j -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
code与msg的枚举
java
package com.example.springbootuniformrespandexceptionhandle.params;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RespCodeEnums {
SUCCESS(0, "Success"),
FAIL(1, "Operation failed."),
FILE_OVER_SIZE(1000, "File over size."),
FILE_FORMAT_WRONG(1001, "File format is wrong."),
PARAM_ERROR(1003, "Request param is wrong."),
JSON_PARSE_ERROR(1004, "Json parse error."),
UNAUTHENTICATION_ERROR(1100, "Not authenticate."),
UNAUTHORIZED_ERROR(1101, "Not Authorized."),
REQUEST_METHOD_ERROR(1201, "Request method wrong."),
API_NOT_EXIST(1301, "Api not exist."),
TIMESTAMP_ERROR(1401, "Timestamp wrong."),
DATA_NOT_EXIST(1501, "Data not exist."),
REDIS_CONN_ERROR(2001, "Redis Conn wrong."),
REDIS_OPER_ERROR(2002, "Redis operate wrong."),
MYSQL_CONN_ERROR(2101, "MySQL Conn wrong."),
MYSQL_QUERY_TIMEOUT(2102, "MySQL Query timeout."),
MYSQL_DATA_NOT_EXIST(2103, "MySQl Cannot find data."),
ELASTICSEARCH_CONN_ERROR(2201, "Redis Conn wrong."),
ELASTICSEARCH_QUERY_TIMEOUT(2202, "Elasticsearch query timeout.");
private int code;
private String msg;
}
上面的 枚举 只是很小的一部分,实际的项目开发具体分析,这部分具体内容请结合自己项目编码。
封装统一的响应体
java
package com.example.springbootuniformrespandexceptionhandle.params;
import lombok.Data;
@Data
public class RespInfo<T> {
private int code;
private String msg;
private T data;
/**
* 全参方法
* @param code
* @param msg
* @param data
* @param <T>
* @return
*/
private static <T> RespInfo<T> response(int code, String msg, T data) {
RespInfo<T> respInfo = new RespInfo<>();
respInfo.setCode(code);
respInfo.setMsg(msg);
respInfo.setData(data);
return respInfo;
}
/**
* 缺失data方法
* @param code
* @param msg
* @param <T>
* @return
*/
private static <T> RespInfo<T> response(int code, String msg) {
RespInfo<T> respInfo = new RespInfo<>();
respInfo.setCode(code);
respInfo.setMsg(msg);
return respInfo;
}
public static <T> RespInfo<T> success() {
return response(RespCodeEnums.SUCCESS.getCode(), RespCodeEnums.SUCCESS.getMsg());
}
public static <T> RespInfo<T> success(T data) {
return response(RespCodeEnums.SUCCESS.getCode(), RespCodeEnums.SUCCESS.getMsg(), data);
}
public static <T> RespInfo<T> fail() {
return response(RespCodeEnums.FAIL.getCode(), RespCodeEnums.FAIL.getMsg());
}
public static <T> RespInfo<T> fail(int code, String msg) {
return response(code, msg);
}
public static <T> RespInfo<T> fail(int code, String msg, T data) {
return response(code, msg, data);
}
}
我们主要限定三个字段,code、msg、data,其中 data 具体的数据结构我们不太清楚,所以这里引入泛型,在实际的项目开发中,这个也很常见。
另外,我们实现了 成功返回的默认、带数据的成功返回,失败返回默认。待具体描述的失败返回等,这些看下代码就一目了然。
示例
这里我们实现一个简单的用户信息管理的控制器类:
kotlin
package com.example.springbootuniformrespandexceptionhandle.controller;
import com.example.springbootuniformrespandexceptionhandle.model.User;
import com.example.springbootuniformrespandexceptionhandle.params.RespCodeEnums;
import com.example.springbootuniformrespandexceptionhandle.params.RespInfo;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/user")
public class UserController {
// 存在于运行内存中
private static Map<String, Object> users = new ConcurrentHashMap<>();
@PostMapping("/add")
public RespInfo addUser(@RequestBody User user) {
users.put(user.getName(), user);
return RespInfo.success();
}
@PostMapping("/info")
public RespInfo getInfo(@RequestBody User user) {
if (!users.containsKey(user.getName())) {
return RespInfo.fail(RespCodeEnums.DATA_NOT_EXIST.getCode(), RespCodeEnums.DATA_NOT_EXIST.getMsg());
}
return RespInfo.success(users.get(user.getName()));
}
@GetMapping("/allUsers")
public RespInfo getAllInfo() {
return RespInfo.success(users);
}
}
可以添加用户,查询某个用户信息,查询所有用户信息,相关的用户 bean如下:
java
package com.example.springbootuniformrespandexceptionhandle.model;
import lombok.Data;
@Data
public class User {
private String name;
private int age;
private String phone;
}
以下是测试情况:
未查询到数据
查询到数据
获取所有数据