如何优雅的处理SpringBoot接口的响应体

我们知道在开发,规范化的开发会节省很多时间,无论是代码规范还是接口规范,如果你们的开发模式是前后端分离的,那你要和前端开发工程师进行对接,不可能因为每个人的代码风格不统一来进行更改,统一的规范会让我们在开发过程事半功倍,下面就看看如何统一处理响应体。

注意】在编写实体类的时候 为了简洁 ,我使用了 lombok 用 @Data 、@Setter 、@Getter 取消了get  set 方法,如果你们觉得不习惯,可以自己手动添加。

以下对响应体的处理,不是绝对唯一的,可根据自己的业务需要,自己定义对应的响应方式。

一、定义一个响应实体类

定义了响应的实体类,code 代表响应状态码,msg代表响应信息,data  代表响应的具体数据 ,这里使用了泛型,为了可以更好的兼容不同类型的返回数据。

package com.dongl.utils.response;

import lombok.Data;
/**
 * @author D-L
 * @Version 1.0
 * @Description 响应体实体类
 * @Date 2020/8/26
 */

@Data
public class ResultVO<T> {
    /**
     * 状态码,比如200代表响应成功
     */
    private int code;
    /**
     * 响应信息,用来说明响应情况
     */
    private String msg;
    /**
     * 响应的具体数据
     */
    private T data;

    public ResultVO(T data) {
        this(ResultCode.SUCCESS, data);
    }

    public ResultVO(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.data = data;
    }
}

二、定义一个响应状态码枚举类(统一响应状态)

这个响应状态码可以自己根据具体的业务自行定义:

package com.dongl.utils.response;
import lombok.Getter;
/**
 * @author D-L
 * @Version 1.0
 * @Description   响应状态码枚举类
 * @Date 2020/8/26
 */
@Getter
public enum ResultCode {

    SUCCESS(200, "操作成功"),

    FAILED(1001, "响应失败"),

    VALIDATE_FAILED(1002, "参数校验失败"),

    ERROR(5000, "未知错误");

    private int code;
    private String msg;

    ResultCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

还记得之前写过一篇 《如何优雅的处理SpringBoot接口中参数校验》其中有对异常的全局处理,这里可以对返回的响应数据进一步处理:

package com.dongl.utils.error;

import com.dongl.utils.response.ResultCode;
import com.dongl.utils.response.ResultVO;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * @author D-L
 * @Classname ExceptionControllerAdvice
 * @Version 1.0
 * @Description   全局处理参数校验异常返回提示
 * @Date 2020/8/26
 */
@RestControllerAdvice
public class ExceptionControllerAdvice {

    @ExceptionHandler(APIException.class)
    public ResultVO<String> APIExceptionHandler(APIException e) {
        // 注意哦,这里传递的响应码枚举
        return new ResultVO<>(ResultCode.FAILED, e.getMsg());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 注意哦,这里传递的响应码枚举
        return new ResultVO<>(ResultCode.VALIDATE_FAILED, objectError.getDefaultMessage());
    }
}

返回响应体:

{
    "code": 1002,
    "msg": "参数校验失败",
    "data": "需要在20和99之间"
}

三、全局配置响应体(这里需要加上扫描的包)

这样定义完了响应体,就需要你在编写代码的同时,每一个接口都要添加对应的code msg data ,虽然勉强说的过去,但是总感觉差点意思,当然我们可以统一处理,先创建一个类加上注解使其成为全局处理类。然后继承ResponseBodyAdvice接口重写其中的方法,即可对我们的controller进行增强操作。

package com.dongl.utils.response;

import com.dongl.utils.error.APIException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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 D-L
 * @date 2020-08-27
 * @Description 全局配置 响应体
 * Version 1.0
 */
@RestControllerAdvice(basePackages = {"com.dongl.controller"}) // 注意哦,这里要加上需要扫描的包
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
        // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false
        return !returnType.getGenericParameterType().equals(ResultVO.class);
    }

    @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)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 将数据包装在ResultVO里后,再转换为json字符串响应给前端
                return objectMapper.writeValueAsString(new ResultVO<>(data));
            } catch (JsonProcessingException e) {
                throw new APIException("返回String类型错误");
            }
        }
        // 将原本的数据包装在ResultVO里
        return new ResultVO<>(data);
    }
}

【总结】响应体的定义,以及状态码的定义,都可以自己根据自己的需要和业务的需要自行编写。

原文地址:https://www.cnblogs.com/dongl961230/p/13569977.html