spring mvc统一API返回格式

对于前后端分离的项目,采用统一的返回格式可以有效减少前后端开发人员的交流成本,对于Spring MVC可以利用切面无侵入地、优雅地实现这一点。

Spring MVC提供了AbstractMappingJacksonResponseBodyAdvice抽象类对返回的JSON做二次处理

首先定义返回结构

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResponse {

    private Integer status;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String error;
    private String message;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Object data;

    public static CommonResponse success(Integer status, Object data) {
        return new CommonResponse(status, null, "success", data);
    }

    public static CommonResponse success(Integer status, String message, Object data) {
        return new CommonResponse(status, null, message, data);
    }

    public static CommonResponse fail(Integer status, String error, String message, Object data) {
        return new CommonResponse(status, error, message, data);
    }

}

编写切面继承AbstractMappingJacksonResponseBodyAdvice并注入容器

@RestControllerAdvice
public class GlobalResponseAdvice extends AbstractMappingJacksonResponseBodyAdvice {

    @Override
    protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
        HttpStatus code = HttpStatus.OK; //默认状态码为200
        final ResponseStatus responseStatus = returnType.getMethodAnnotation(ResponseStatus.class); //这里反射获取Controller方法的ResponseStatus注解,如果存在注解,则使用该注解的值作为状态码。作为Restful API 201状态码也是很常用的
        if (responseStatus != null) {
            code = responseStatus.code();
        }

        Object body = bodyContainer.getValue();
        if (!CommonResponse.class.isAssignableFrom(body.getClass())) { //对于已经包装为CommonResponse的结果不处理,以防二次包装
            bodyContainer.setValue(CommonResponse.success(code.value(), body));
        }
    }

}

效果

{
    "status": 200,
    "message": "success",
    "data": {
        "id": 1,
        "username": "user1",
        "role": "ADMIN"
    }
}