关于springboot:SpringBoot定义优雅全局统一Restful-API-响应框架六

闲话不多说,持续优化 全局对立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 都这么写不太敌对。

  1. 返回内容这么不够明确具体
  2. 所有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
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
    /**
     * supports办法: 判断是否要执行beforeBodyWrite办法,
     * true为执行,false不执行.
     * 通过该办法能够抉择哪些类或那些办法的response要进行解决, 其余的不进行解决.
     *
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> 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<? extends HttpMessageConverter<?>> 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 这些内置异样,而是咱们本人定义的异样。

  1. 定义公共异样

    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
     */
    @Data
    public 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;
     }
    
    }
  2. 所有其余异样继承公共异样
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);
    }

}
  1. 断言业务异样类封装
public interface Assert {
    /**
     * 创立异样
     * @param args
     * @return
     */
    BaseException newException(Object... args);

    /**
     * 创立异样
     * @param t
     * @param args
     * @return
     */
    BaseException newException(Throwable t, Object... args);

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

    /**
     * <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异样
     * <p>异样信息<code>message</code>反对传递参数形式,防止在判断之前进行字符串拼接操作
     *
     * @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仓库脚手架我的项目

关注公众号,程序员三时 继续输入优质内容 心愿给你带来一点启发和帮忙

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理