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

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"
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SpringBoot项目实战笔记可以按照以下步骤进行: 1. 首先,你可以通过观看B站上的教程视频来学习SpringBoot项目实战。在视频中,你可以学习到如何使用SpringBoot、MyBatis和MySQL创建一个电脑商城项目。 2. 确保你的SpringBoot项目能够成功启动。找到被@SpringBootApplication注解修饰的入口启动类,并运行该类。如果你能够观察到图形化的界面,那么说明你的项目成功启动了。 3. 如果你还没有创建SpringBoot项目,你可以使用Spring Initializr来初始化一个新的项目Spring Initializr是一个Web应用程序,可以为你生成Spring Boot项目的基本结构。你可以选择使用Maven或Gradle作为构建工具,并添加适合你的项目的依赖。然后,你只需要编写应用程序的代码即可。 希望以上信息对你有帮助!如果还有其他问题,请随时提问。123 #### 引用[.reference_title] - *1* *2* [SpringBoot项目实战笔记:电脑商城项目实战(SpringBoot+MyBatis+MySQL)](https://blog.csdn.net/weixin_44260350/article/details/127746667)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *3* [《SpringBoot实战》读书笔记](https://blog.csdn.net/sanhewuyang/article/details/104494202)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ThinkPet

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

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

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

打赏作者

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

抵扣说明:

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

余额充值