首页 >文化 > 正文

SpringBoot定义优雅全局统一Restful API 响应框架六|环球聚看点

2023-06-13 15:34:25 来源:博客园

闲话不多说,继续优化 全局统一Restful API 响应框架做到项目通用 接口可扩展。


(资料图)

如果没有看前面几篇文章请先看前面几篇

SpringBoot定义优雅全局统一Restful API 响应框架

SpringBoot定义优雅全局统一Restful API 响应框架二

SpringBoot定义优雅全局统一Restful API 响应框架三

SpringBoot定义优雅全局统一Restful API 响应框架四

SpringBoot定义优雅全局统一Restful API 响应框架五

这里讲一讲最后的版本和需要修复的一些问题

@PostMapping("/add/UserApiCombo")    public R addApiCombo(@RequestBody @Validated UserApplyApiComboDto userApplyApiComboDto) {        userApiComboService.addApiCombo(userApplyApiComboDto);        return R.success();    }

我们看看这个代码,有什么问题。 我们返回了统一的封装结果集R但是后面所有的controller都这么写不太友好。

返回内容这么不够明确具体所有controller这么写增加重复工作量

我们可以这么去优化:

Spirng 提供了 ResponseBodyAdvice接口,支持在消息转换器执行转换之前,对接口的返回结果进行处理,再结合 @ControllerAdvice注解即可轻松支持上述功能

package cn.soboys.springbootrestfulapi.common.handler;import cn.hutool.core.bean.BeanUtil;import cn.hutool.core.map.MapUtil;import cn.soboys.springbootrestfulapi.common.error.ErrorDetail;import cn.soboys.springbootrestfulapi.common.resp.R;import lombok.extern.slf4j.Slf4j;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.ControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;/** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/6/12 12:17 下午 * @webSite https://github.com/coder-amiao * @Slf4j * @ControllerAdvice */@Slf4j@ControllerAdvicepublic class ResponseResultHandler implements ResponseBodyAdvice {    /**     * supports方法: 判断是否要执行beforeBodyWrite方法,     * true为执行,false不执行.     * 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.     *     * @param returnType     * @param converterType     * @return     */    @Override    public boolean supports(MethodParameter returnType, Class> converterType) {        return true;    }    /**     * beforeBodyWrite方法: 对response方法进行具体操作处理     * 实际返回结果业务包装处理     *     * @param body     * @param returnType     * @param selectedContentType     * @param selectedConverterType     * @param request     * @param response     * @return     */    @Override    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {        if (body instanceof R) {            return body;        } else if (body == null) {            return R.success();        } else if (body instanceof ErrorDetail) {            return body;        } else if (body instanceof String) {            return body;        } else {            return R.success().data(body);        }    }}

在实际controller返回中我们直接返回数据内容就可以了

@GetMapping("/home")    public Student home() {        Student s = new Student();        s.setUserName("Tom");        s.setAge(22);        List hobby = new ArrayList();        hobby.add("抽烟");        hobby.add("喝酒");        hobby.add("烫头");        s.setHobby(hobby);        s.setBalance(2229891.0892);        s.setIdCard("420222199811207237");        return s;    }

我们目前版本中业务错误判断逻辑不是很友好,还需要优化,这里我们可以封装自己的业务异常用 Assert(断言)封装异常,让代码更优雅

符合 错误优先返回原则

正常我们业务异常代码是这样写的

// 另一种写法        Order order = orderDao.selectById(orderId);        if (order == null) {            throw new IllegalArgumentException("订单不存在。");        }

使用断言优化后

Order order = orderDao.selectById(orderId); Assert.notNull(order, "订单不存在。");

两种方式一对比,是不是明显感觉第一种更优雅,第二种写法则是相对丑陋的 if {...} 代码块。那么 神奇的 Assert.notNull()背后到底做了什么呢?

这里就是我们需要优化代码

其实很多框架都带有Assert工具包括JAVA JDK . SpringBoot,spring 也有自己的Assert但是不符合我们自己的异常抛出业务逻辑,这里我们可以自定义自定的Assert工具

我们来看一下部分源码

public abstract class Assert {    public Assert() {    }    public static void notNull(@Nullable Object object, String message) {        if (object == null) {            throw new IllegalArgumentException(message);        }    }}

可以看到,Assert其实就是帮我们把 if {...}封装了一下,是不是很神奇。虽然很简单,但不可否认的是编码体验至少提升了一个档次。

那么我们是不是可以模仿Assert也写一个自定义断言类,不过断言失败后抛出的异常不是IllegalArgumentException这些内置异常,而是我们自己定义的异常。

定义公共异常
package cn.soboys.springbootrestfulapi.common.exception;import cn.soboys.springbootrestfulapi.common.resp.ResultCode;import lombok.Data;/** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/6/12 10:32 下午 * @webSite https://github.com/coder-amiao */@Datapublic class BaseException extends RuntimeException {    /**     * 返回码     */    protected ResultCode resultCode;    /**     * 异常消息参数     */    protected Object[] args;    public BaseException(ResultCode resultCode) {        super(resultCode.getMessage());        this.resultCode = resultCode;    }    public BaseException(String code, String msg) {        super(msg);        this.resultCode = new ResultCode() {            @Override            public String getCode() {                return code;            }            @Override            public String getMessage() {                return msg;            }            @Override            public boolean getSuccess() {                return false;            }            ;        };    }    public BaseException(ResultCode resultCode, Object[] args, String message) {        super(message);        this.resultCode = resultCode;        this.args = args;    }    public BaseException(ResultCode resultCode, Object[] args, String message, Throwable cause) {        super(message, cause);        this.resultCode = resultCode;        this.args = args;    }}
所有其他异常继承公共异常
package cn.soboys.springbootrestfulapi.common.exception;import cn.soboys.springbootrestfulapi.common.resp.ResultCode;/** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/4/29 00:15 * @webSite https://github.com/coder-amiao * 通用业务异常封装 */public class BusinessException extends BaseException {        public BusinessException(ResultCode resultCode, Object[] args, String message) {        super(resultCode, args, message);    }    public BusinessException(ResultCode resultCode, Object[] args, String message, Throwable cause) {        super(resultCode, args, message, cause);    }}
断言业务异常类封装
public interface Assert {    /**     * 创建异常     * @param args     * @return     */    BaseException newException(Object... args);    /**     * 创建异常     * @param t     * @param args     * @return     */    BaseException newException(Throwable t, Object... args);    /**     * 

断言对象obj非空。如果对象obj为空,则抛出异常 * * @param obj 待判断对象 */ default void assertNotNull(Object obj) { if (obj == null) { throw newException(obj); } } /** *

断言对象obj非空。如果对象obj为空,则抛出异常 *

异常信息message支持传递参数方式,避免在判断之前进行字符串拼接操作 * * @param obj 待判断对象 * @param args message占位符对应的参数列表 */ default void assertNotNull(Object obj, Object... args) { if (obj == null) { throw newException(args); } }}

具体使用

/**     * 异常返回模拟     *     * @return     */    @GetMapping("/exception")    public Student  exception() {        Student s = null;        BusinessErrorCode.Sign_Error.assertNotNull(s,"secret秘钥不正确");        return s;    }

在业务中我们可以通过这个方式直接抛出枚举异常。这样代码就简洁干净很多

代理已经更新到 github仓库脚手架项目

关注公众号,程序员三时持续输出优质内容 希望给你带来一点启发和帮助

标签:

x 广告
x 广告

Copyright ©   2015-2022 西南影视网版权所有  备案号:皖ICP备2022009963号-8   联系邮箱:39 60 29 14 2@qq.com