在对接第三方接口的时候,第三方接口返回格式形式为{"result":null,"status":1010},虽然返回了状态码,但是状态码对应的描述信息并没有携带,前端在使用的时候需要根据状态码返回一个友好的提示,如此一来,最好的解决办法就是在返回值内将返回码对应的提示信息一并返回给前端。
这样就需要后端在接口返回格式上做统一处理了。因为原有的后端框架在处理接口返回格式上已经有了一套格式处理的逻辑,因此我是在借助@RestControllerAdvice的前提下在原有返回格式上进一步修改的。
先把原有的接口统一格式返回做个说明。
一、定义状态码接口
public interface BaseInfoInterface {
//错误码
String getResultCode();
//错误信息
String getResultMsg();
}
二、定义枚举类,枚举各状态码
public enum CommonEnum implements BaseInfoInterface {
//数据操作错误定义
SUCCESS("200", "成功!"),
BODY_NOT_MATCH("400", "请求的数据格式不符!"),
SIGNATURE_NOT_MATCH("401", "请求的数字签名不匹配!"),
NOT_FOUND("404", "未找到该资源!"),
INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
SERVER_BUSY("503", "服务器正忙,请稍后再试!");
//响应码
private String resultCode;
//响应描述
private String resultMsg;
CommonEnum(String resultCode,String resultMsg){
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
@Override
public String getResultCode() {
return resultCode;
}
@Override
public String getResultMsg() {
return resultMsg;
}
}
三、书写ResultBody包装类
import com.alibaba.fastjson2.JSONObject;
/**
* 自定义返回数据格式
*/
public class ResultBody {
//响应代码
private String code;
//响应消息
private String message;
//响应结果
private Object result;
public ResultBody() {
}
public ResultBody(BaseInfoInterface infoInterface) {
this.code = infoInterface.getResultCode();
this.message = infoInterface.getResultMsg();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
/**
* 成功
*
* @param data
* @return
*/
public static ResultBody success(Object data) {
ResultBody body = new ResultBody();
body.setCode(CommonEnum.SUCCESS.getResultCode());
body.setMessage(CommonEnum.SUCCESS.getResultMsg());
body.setResult(data);
return body;
}
/**
* 失败
*
* @param errorInfoInterface
* @return
*/
public static ResultBody error(BaseInfoInterface infoInterface) {
ResultBody body = new ResultBody();
body.setCode(infoInterface.getResultCode());
body.setMessage(infoInterface.getResultMsg());
body.setResult(null);
return body;
}
/**
* 失败
*/
public static ResultBody error(String code, String message) {
ResultBody rb = new ResultBody();
rb.setCode(code);
rb.setMessage(message);
rb.setResult(null);
return rb;
}
/**
* 失败
* @param message
* @return
*/
public static ResultBody error(String message) {
ResultBody body = new ResultBody();
body.setCode("-1");
body.setMessage(message);
body.setResult(null);
return body;
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
上述封装后,接口返回格式使用ResultBody即可。上述封装能解决大部分问题,但是正如我所碰到的问题,在调用第三方接口的时候就不是很友好的。除此之外,接口每次书写,都需要写return new ResultBody.success().......这样也显得麻烦,因此在原有基础上,我对返回进行了统一处理
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.smile.dawnexcel.annotation.NotControllerResponseAdvice;
import com.smile.dawnexcel.utils.ResultBody;
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;
/**
* 接口返回结果,全局统一处理
*/
@RestControllerAdvice(basePackages = {"com.smile.dawnexcel.controller"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
//response是ResultVo类型
return !returnType.getParameterType().isAssignableFrom(ResultBody.class);
}
// @SneakyThrows
@Override
public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest request, ServerHttpResponse response) {
//String类型不能直接包装
if (returnType.getGenericParameterType().equals(String.class)) {
try {
//将String类型的数据转换为JSONObject,并将响应码对应的描述查询到添加后返回
JSONObject jsonObject = JSONObject.parseObject(data.toString());
//根据状态码查询到对应的描述信息
String msg = this.resultManagerMapper.queryMsg((Integer) jsonObject.get("status"));
jsonObject.put("msg", msg);
return ResultBody.success(jsonObject);
} catch (Exception e) {
e.printStackTrace();
}
}
//否则直接包装成ResultVo返回
return new ResultBody(data);
}
}
说明:
1、@RestControllerAdvice(basePackages={“com.smile.dawnexcel.controller”})自动扫描了所有指定包写的controller,在response时进行统一处理;
2、重写supports方法,也就是说,当返回类型已经时ResultVo了,那就不需要封装了,当不等于ResultVo时才进行调用beforeBodyWrite方法,跟过滤器的效果是一样的。当结果为true的时候才进行调用beforeBodyWrite方法;
3、最后重写我们的封装方法beforeBodyWrite方法,注意处理String的返回值有点特殊,无法直接封装成json,我们需要进行特殊处理,其它的直接new ResultVo(data)
4、完成上述配置后使用时就不需要每次都return new ResultVo了
完成统一响应后,需要考虑的是,有些不需要统一响应的接口该怎么办,所以需要一个注解来区分不需要统一响应
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 不进行结果封装的注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
}
然后在配置中排除该注解下的接口