前端在调用后端接口时往往不同的接口返回的数据是不一样的,但是通常我们会与前端约定一个固定的返回格式,通过固定的格式告诉他们什么时候接口是返回成功,什么时候返回失败,返回成功后他们如何拿到接口返回的数据去渲染前端页面。
在不对接口统一包装时,你可能会这样的返回如一个查询接口
@PostMapping(value = "/list")
public List<Map<String, Object>> list() {
List<Map<String, Object>> mapList = new ArrayList<>();
HashMap<String, Object> map = new HashMap<>();
map.put("dazhi", "大智");
map.put("zhangsan", "张三");
map.put("laoliu", "老六");
mapList.add(map);
return mapList;
}
接口返回
又或者这样的如一个删除接口
@PostMapping(value = "/delete")
public String delete() {
return "删除完毕";
}
接口返回
像这些情况如果你和前端开发人员联调接口她们就会很懵逼,由于我们没有给他一个统一的格式,前端人员不知道如何处理返回值。所以我们需要定义一个统一的标准返回格式的。
一个标准的返回格式至少包含3部分:
code 状态码:统一定义各种返回结果的状态码。
msg 描述:接口调用的结果描述。
data 数据:返回的数据。
{
"code": "S000",
"msg": "操作成功!",
"data": "数据更新成功!"
}
我们使用@RestControllerAdvice注解并且实现ResponseBodyAdvice接口来实现统一返回格式的封装,关于使用@RestControllerAdvice实现全局异常处理可以看我以前的文章《Springboot全局异常处理从配置文件中读取自定义异常信息》,接下来展示一下大概的代码
定义一个标准的统一返回实体
package com.yx.limit.base.vo;
import com.yx.limit.base.enums.ResponseEnum;
import lombok.Data;
/**
* @Author yx
* @Description 统一响应实体
*/
@Data
public class ResponseVo<T> {
/**
* 响应编码
*/
private String code;
/**
* 消息内容
*/
private String msg;
/**
* 响应数据
*/
private T data;
private ResponseVo() {
}
private enum Singleton{
INSTANCE;
private ResponseVo responseVo;
Singleton(){
if (responseVo == null){
responseVo = new ResponseVo();
}
}
public ResponseVo getInstance(){
return responseVo;
}
}
public static <T> ResponseVo<T> success() {
return success(null);
}
public static <T> ResponseVo<T> success(T data) {
ResponseVo<T> responseVo = Singleton.INSTANCE.getInstance();
responseVo.setCode(ResponseEnum.SUCCESS.getResultCode());
responseVo.setMsg(ResponseEnum.SUCCESS.getResultMsg());
responseVo.setData(data);
return responseVo;
}
public static <T> ResponseVo<T> error(String code, String msg) {
ResponseVo<T> responseVo = Singleton.INSTANCE.getInstance();
responseVo.setCode(code);
responseVo.setMsg(msg);
responseVo.setData(null);
return responseVo;
}
public static <T> ResponseVo<T> error(String msg) {
ResponseVo<T> responseVo = Singleton.INSTANCE.getInstance();
responseVo.setCode(ResponseEnum.ERROR.getResultCode());
responseVo.setMsg(ResponseEnum.ERROR.getResultMsg());
responseVo.setData(null);
return responseVo;
}
}
定义一个状态码枚举
package com.yx.limit.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Author yx
* @Description 响应编码枚举
*/
@AllArgsConstructor
public enum ResponseEnum {
/**
* 数据操作错误定义
*/
SUCCESS("S000","操作成功!"),
ERROR("E000","操作失败!"),
SIGNATURE_NOT_MATCH("E001","请求的数字签名不匹配!"),
BODY_NOT_MATCH("E002","请求的数据格式不符!"),
INTERNAL_SERVER_ERROR("E003", "服务器内部错误!");
/**
* 错误码
*/
@Getter
private String resultCode;
/**
* 错误描述
*/
@Getter
private String resultMsg;
}
定义一个忽略自动包装返回功能的注解
package com.yx.limit.base.annotations;
import java.lang.annotation.*;
/**
* @Author yx
* @Description 忽略自动返回构造注解
*/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreAutoResponse {
}
统一响应结果通知类
package com.yx.light.element.mybatis.advice;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yx.limit.base.annotations.IgnoreAutoResponse;
import com.yx.limit.base.vo.ResponseVo;
import lombok.SneakyThrows;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @Author yx
* @Description 统一响应结果通知
*/
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
private final ObjectMapper objectMapper;
public ResponseAdvice(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return !methodParameter.hasMethodAnnotation(IgnoreAutoResponse.class);
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//SpringBoot的Controller中的方法返回值是String时会直接返回
if (o instanceof String) {
return objectMapper.writeValueAsString(ResponseVo.success(o));
}
//全局异常处理的结果直接返回即可
if (o instanceof ResponseVo) {
return o;
}
return ResponseVo.success(o);
}
}
这个时候我们再来请求原来的两个方法查看返回结果
list方法:
delete方法:
我们发现delete的方法返回的json格式没有格式化,我们只需要稍加改造一下delete方法就能正常返回格式化的数据
@PostMapping(value = "/delete", produces = "application/json; charset=UTF-8")
public String delete() {
return "删除完毕";
}
对于一些不想返回固定包装的方法可以在方法上加上@IgnoreAutoResponse注解即可返回原始的格式
@PostMapping(value = "/original")
@IgnoreAutoResponse
public String original() {
return "原始字符串";
}