springboot项目用ResponseBodyAdvice统一包装返回体-笔记

文章介绍了如何在SpringBoot项目中利用ResponseBodyAdvice接口来统一包装RESTAPI的返回数据,这样可以简化后端工作,方便数据结构的变更。通过定义一个实现了ResponseBodyAdvice接口的类,并在Controller中应用,可以自动对所有返回体进行增强处理,确保返回的数据结构一致,同时也便于处理异常和日志记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot项目用ResponseBodyAdvice统一包装返回体-笔记

在使用springboot开发rest api接口时,往往需要和前端对接定义返回的数据结构,这时使用ResponseBodyAdvice统一包装返回体给前端,可以大大减轻后端的工作量,同时也方便对返回体的数据结构做灵活变更。
使用ResponseBodyAdvice封装好统一的返回体后,Controller中就可以专心处理业务模型和业务数据了。

定义1个ResponseBodyAdvice接口的实现类

/**
 * 实现 ResponseBodyAdvice接口,并重写其中的方法,即可对我们的controller进行增强操作
 *
 * 不管Controller中 是否使用ResultVO<T>对返回结果包装,
 * 均可被 ResponseBodyAdvice接口 处理返回数据,以使系统 统一结果,统一异常,统一日志
 */
// 注意哦,这里要加上需要扫描的包
@RestControllerAdvice(basePackages = {"cn.ath.knowwikibackend.rest"})
public class SysRestBodyAdvice implements ResponseBodyAdvice<Object> {
    /**
     * Whether this component supports the given controller method return type
     * and the selected {@code HttpMessageConverter} type.
     *
     * supports方法要返回为true才会执行beforeBodyWrite方法,
     * 所以如果有些情况不需要进行增强操作可以在supports方法里进行判断
     *
     * @param returnType    the return type
     * @param converterType the selected converter type
     * @return {@code true} if {@link #beforeBodyWrite} should be invoked;
     * {@code false} otherwise
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false
        return !returnType.getGenericParameterType().equals(ResultVO.class);
    }

    /**
     * Invoked after an {@code HttpMessageConverter} is selected and just before
     * its write method is invoked.
     *
     * 对返回数据进行真正的操作还是在beforeBodyWrite方法中,
     * 我们可以直接在该方法里包装数据,这样就不需要每个接口都进行数据包装了,省去了很多麻烦
     *
     * @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
     */
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

        // String类型不能直接包装,所以要进行些特别的处理
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 将数据包装在ResultVO里后,再转换为json字符串响应给前端
                return objectMapper.writeValueAsString(new ResultVO<>(body));
            } catch (JsonProcessingException e) {
                throw new APIException("返回String类型错误");
            }
        }
        // 将原本的数据包装在ResultVO里
        return new ResultVO<>(body);
    }
}

用到的ResultVO类

import cn.hutool.core.date.DateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class ResultVO<T> {
    int code;
    String msg;
    T content;
    Long timestamp;
    String traceDateTime;
    public ResultVO(int code,String msg,T content){
        this.code = code;
        this.msg = msg;
        this.content = content;
        this.timestamp = System.currentTimeMillis();
        this.traceDateTime = DateTime.now().toString();
    }

    public ResultVO(int code,String msg){
        this.code = code;
        this.msg = msg;
        this.content = null;
        this.timestamp = System.currentTimeMillis();
        this.traceDateTime = DateTime.now().toString();

    }

    public ResultVO(ResultCodeEnum resultCode, T content) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.content = content;
        this.timestamp = System.currentTimeMillis();
        this.traceDateTime = DateTime.now().toString();


    }

    public ResultVO(T content){
        this(ResultCodeEnum.SUCCESS, content);
    }
}

用到的ResultCodeEnum枚举

import lombok.Getter;

@Getter
public enum ResultCodeEnum {
    SUCCESS(0, "操作成功"),
    VERITY_GEN_ERROR(9801,"验证码生成失败"),
    VERITY_IDENTITY_ERROR(9803,"验证码验证失败"),
    TOKEN_NULL(9777, "请求头里没有token"),
    TOKEN_TIME_OUT(9776, "token已过期,请重新登录"),
    TOKEN_USER_ALREADY_SIGN(9775, "token用户已登录,请退出重登后再请求本接口"),
    FAILED(9500, "响应失败"),
    BUSY(9600, "系统繁忙"),
    ACCOUNT_PASS_FAILED(9301, "密码错误"),
    ACCOUNT_NOT_EXIST(9302, "用户名不存在"),
    ACCOUNT_DISABLED(9303, "用户没有权限/已禁用"),
    NOT_LOGIN(9403,"未登录"),
    REQ_BODY_IS_NULL(9995,"requestBody请求体异常"),
    VALIDATE_FAILED(9996, "参数校验异常"),
    ARITHMETIC_FAILED(9997, "算术异常"),
    ARRAY_INDEX_OUT_OF_BOUNDS_FAILED(9998, "数组越界异常"),
    USER_IS_USING(7701,"目标用户正在使用中"),
    IllEGAL_ARG_EXCEPTION(9988,"非法参数异常"),
    ERROR(9999, "未知错误");

    private int code;
    private String msg;

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

}

在Controller里写rest api接口,不需要手动封装ResultVO,直接返回业务对象即可

@Slf4j
@RestController
public class Test0529Api{
    @PostMapping("/t123")
    public UserReq test123(@Valid @RequestBody UserReq req){
        log.info("req:{}",req.toString());
        return req;
    }
    
    @PostMapping("/alterUser")
    public UserTestResp  alterUser(@RequestBody  @Validated({UpdateActionGroup.class})
                                               UserTestReq1 req ){
        log.info("req:{}",req);
        return UserTestResp.builder()
                .id(req.getId())
                .age(req.getAge())
                .name(req.getName())
                .build();
    }
}

返回的json数据如下

{
    "code": 0,
    "msg": "操作成功",
    "content": [
        {
            "account": "wr23r",
            "email": "wr22r12we@163.com",
            "phone": "13531323021",
            "startDt": "2022-05-06 13:14:15",
            "endDt": "2023-06-06 13:30:00",
            "company": {
                "id": 3,
                "name": "er23r",
                "tel": "02131470060"
            },
            "testCount": 32
        }
    ],
    "timestamp": 1685352446296,
    "traceDateTime": "2023-05-29 17:27:26"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ThinkPet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值