springboot是如何实现统一的返回格式

前言 

项目开发中,经常需要后端通常要给前端返回数据,后端程序员也经常会对数据进行自行封装,如果没有一个统一的规范,每个人都会自定义返回格式,这样一整,前端就会懵逼,骂娘,为了规避这种情况,就需要统一规范,统一给出相应数据格式,本文带你来看看springboot是如何实现的。

附录

参考的博客:SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的! - 掘金

springboot常见返回格式

第一种,String

@GetMapping(value = "/hello1")
public String test5(){
   return "hello world";
}
http://localhost:8080/hello1
hello world

第二种,json

@GetMapping(value = "/hello2")
public ValidVO test6(){
    ValidVO vo = new ValidVO();
    vo.setName("zhangsan");
    vo.setSex("男");
    return vo;
}
http://localhost:8080/hello2
{
    "id": null,
    "appId": null,
    "name": "zhangsan",
    "email": null,
    "sex": "男",
    "level": null
}

第三种,异常形式

@GetMapping(value = "/hello3")
public void test7(){
   throw new RuntimeException("asdf");
}
http://localhost:8080/hello3
{
    "timestamp": "2021-12-08T09:11:51.371+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "",
    "path": "/hello3"
}

如何统一返回格式

这里我们采用spring里面的@RestControllerAdvice+自定义注解的形式来实现。

具体思路如下。

第一步,定义统一响应实体类

这里我们就要思考,一个标准的响应都有哪些信息。

这里我们采用大多数公司采用的格式。

  1. code响应码:由后端统一定义各种返回结果的状态码
  2. msg描述:本次接口调用的结果描述
  3. data 数据:本次返回的数据
{
  "code":"0",
  "msg":"成功",
  "data":[{
        "name":"张三",
        "age":"31"
  }]
}

第二步,定义统一响应码

响应码的作用是让人快速知道返回的结果状态,见名知意。通常分为三大类,成功,失败,异常。

一些项目会有更专业的响应码,会对异常信息和相对应的错误码绑定,通过报出的错误码,查询错误码手册来获知项目的报错内容,从而定位问题,避免盲目。这样也使代码更优雅美观。

@Data
public class CommonResp<T> implements Serializable {
    private static final long serivalVersionUID = 1L;

    /**
     * 响应码
     */
    private String code = "0";

    /**
     * 响应信息
     */
    private String msg = "success";

    /**
     * 响应消息体
     */
    private T data;

    private CommonResp(){
        this.code = ResponseCodeEnum.SUCCESS.getCode();
        this.msg =  ResponseCodeEnum.SUCCESS.getMsg();
    }

    private static CommonResp success(Object data){
        CommonResp result = new CommonResp();
        result.setData(data);
        return result;
    }
}

第三步,把统一返回体封装成注解

springboot提供了一个接口ResponseBodyAdvice。

这个接口是起什么作用呢?

先看一波源码,如下

/**
 *Allows customizing the response after the execution of an @ResponseBody or a 
 *ResponseEntity controller method but before the body is written with an 
 *HttpMessageConverter.
 *Implementations may be registered directly with RequestMappingHandlerAdapter and 
 *ExceptionHandlerExceptionResolver or more likely annotated with @ControllerAdvice in 
 *which case they will be auto-detected by both.
 *Since:
 *4.1
 *Author:
 *Rossen Stoyanchev
 *Type parameters:
 *<T> – the body type
 */
public interface ResponseBodyAdvice<T> {

	/**
	 * Whether this component supports the given controller method return type
	 * and the selected {@code HttpMessageConverter} type.
	 * @param returnType the return type
	 * @param converterType the selected converter type
	 * @return {@code true} if {@link #beforeBodyWrite} should be invoked;
	 * {@code false} otherwise
	 */
	boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

	/**
	 * Invoked after an {@code HttpMessageConverter} is selected and just before
	 * its write method is invoked.
	 * @param body the body to be written
	 * @param returnType the return type of the controller method
	 * @param selectedContentType the content type selected through content negotiation
	 * @param selectedConverterType the converter type selected to write to the response
	 * @param request the current request
	 * @param response the current response
	 * @return the body that was passed in or a modified (possibly new) instance
	 */
	@Nullable
	T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType,
			ServerHttpRequest request, ServerHttpResponse response);

}

来我给你翻译翻译:拦截Controller方法的返回值,统一处理返回值/响应体,一般用来统一返回格式,加解密,签名等等。

那么这个玩意怎么用呢?

我们只需实现这个接口,即可。

@RestControllerAdvice
public class CommonRespAdvice implements ResponseBodyAdvice<Object>{

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        boolean hasMethodAnnotation = returnType.hasMethodAnnotation(WrapCommonResp.class);
        if(!hasMethodAnnotation){
            WrapCommonResp wrapCommonResp = returnType.getMethod() == null ? null : returnType.getMethod().getDeclaringClass().getAnnotation(WrapCommonResp.class);
            if(wrapCommonResp != null){
                hasMethodAnnotation = true;
            }
        }
        return hasMethodAnnotation;
    }

    @Override
    public Object beforeBodyWrite(Object obj, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(obj instanceof String){
            return JSON.toJSONString(CommonResp.success(obj));
        }
        return CommonResp.success(obj);
    }
}

以上还涉及到一个注解:@RestControllerAdvice

这个注解是是 @RestController注解的增强,可以实现三个方面的功能:

  1. 全局异常处理
  2. 全局数据绑定
  3. 全局数据预处理
if(obj instanceof String){
    return JSON.toJSONString(CommonResp.success(obj));
}

注:这段代码一定要加,因为springboot对于string类型是直接返回的。

统一格式后的返回结果

{
    "code": "0",
    "msg": "success",
    "data": {
        "id": null,
        "appId": null,
        "name": "zhangsan",
        "email": null,
        "sex": "男",
        "level": null
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值