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

110次阅读

共计 5734 个字符,预计需要花费 15 分钟才能阅读完成。

闲话不多说,持续优化 全局对立 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 仓库脚手架我的项目

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

正文完
 0