閑話(huà)不多說(shuō),繼續(xù)優(yōu)化 全局統(tǒng)一Restful API 響應(yīng)框架 做到項(xiàng)目通用 接口可擴(kuò)展。
如果沒(méi)有看前面幾篇文章請(qǐng)先看前面幾篇
SpringBoot定義優(yōu)雅全局統(tǒng)一Restful API 響應(yīng)框架
SpringBoot定義優(yōu)雅全局統(tǒng)一Restful API 響應(yīng)框架二
SpringBoot定義優(yōu)雅全局統(tǒng)一Restful API 響應(yīng)框架三
SpringBoot定義優(yōu)雅全局統(tǒng)一Restful API 響應(yīng)框架四
SpringBoot定義優(yōu)雅全局統(tǒng)一Restful API 響應(yīng)框架五
這里講一講最后的版本和需要修復(fù)的一些問(wèn)題
@PostMapping("/add/UserApiCombo")
public R addApiCombo(@RequestBody @Validated UserApplyApiComboDto userApplyApiComboDto) {
userApiComboService.addApiCombo(userApplyApiComboDto);
return R.success();
}
我們看看這個(gè)代碼,有什么問(wèn)題。 我們返回了統(tǒng)一的封裝結(jié)果集R
但是后面所有的controller
都這么寫(xiě)不太友好。
- 返回內(nèi)容這么不夠明確具體
- 所有
controller
這么寫(xiě)增加重復(fù)工作量
我們可以這么去優(yōu)化:
Spirng 提供了 ResponseBodyAdvice
接口,支持在消息轉(zhuǎn)換器執(zhí)行轉(zhuǎn)換之前,對(duì)接口的返回結(jié)果進(jìn)行處理,再結(jié)合 @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 公眾號(hào) 程序員三時(shí)
* @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方法: 判斷是否要執(zhí)行beforeBodyWrite方法,
* true為執(zhí)行,false不執(zhí)行.
* 通過(guò)該方法可以選擇哪些類(lèi)或那些方法的response要進(jìn)行處理, 其他的不進(jìn)行處理.
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
/**
* beforeBodyWrite方法: 對(duì)response方法進(jìn)行具體操作處理
* 實(shí)際返回結(jié)果業(yè)務(wù)包裝處理
*
* @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);
}
}
}
在實(shí)際controller
返回中我們直接返回?cái)?shù)據(jù)內(nèi)容就可以了
@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;
}
我們目前版本中業(yè)務(wù)錯(cuò)誤判斷邏輯不是很友好,還需要優(yōu)化,這里我們可以封裝自己的業(yè)務(wù)異常
用 Assert(斷言) 封裝異常,讓代碼更優(yōu)雅
符合 錯(cuò)誤優(yōu)先返回原則
正常我們業(yè)務(wù)異常代碼是這樣寫(xiě)的
// 另一種寫(xiě)法
Order order = orderDao.selectById(orderId);
if (order == null) {
throw new IllegalArgumentException("訂單不存在。");
}
使用斷言?xún)?yōu)化后
Order order = orderDao.selectById(orderId);
Assert.notNull(order, "訂單不存在。");
兩種方式一對(duì)比,是不是明顯感覺(jué)第一種更優(yōu)雅,第二種寫(xiě)法則是相對(duì)丑陋的 if {...} 代碼塊。那么 神奇的 Assert.notNull() 背后到底做了什么呢?
這里就是我們需要優(yōu)化代碼
其實(shí)很多框架都帶有Assert 工具包括JAVA JDK . SpringBoot,spring 也有自己的Assert
但是不符合我們自己的異常拋出業(yè)務(wù)邏輯,這里我們可以自定義自定的Assert 工具
我們來(lái)看一下部分源碼
public abstract class Assert {
public Assert() {
}
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
}
可以看到,Assert 其實(shí)就是幫我們把 if {...} 封裝了一下,是不是很神奇。雖然很簡(jiǎn)單,但不可否認(rèn)的是編碼體驗(yàn)至少提升了一個(gè)檔次。
那么我們是不是可以模仿Assert也寫(xiě)一個(gè)自定義斷言類(lèi),不過(guò)斷言失敗后拋出的異常不是IllegalArgumentException 這些內(nèi)置異常,而是我們自己定義的異常。
- 定義公共異常
package cn.soboys.springbootrestfulapi.common.exception;
import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
import lombok.Data;
/**
* @author 公眾號(hào) 程序員三時(shí)
* @version 1.0
* @date 2023/6/12 10:32 下午
* @webSite https://github.com/coder-amiao
*/
@Data
public class BaseException extends RuntimeException {
/**
* 返回碼
*/
protected ResultCode resultCode;
/**
* 異常消息參數(shù)
*/
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 公眾號(hào) 程序員三時(shí)
* @version 1.0
* @date 2023/4/29 00:15
* @webSite https://github.com/coder-amiao
* 通用業(yè)務(wù)異常封裝
*/
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);
}
}
- 斷言業(yè)務(wù)異常類(lèi)封裝
public interface Assert {
/**
* 創(chuàng)建異常
* @param args
* @return
*/
BaseException newException(Object... args);
/**
* 創(chuàng)建異常
* @param t
* @param args
* @return
*/
BaseException newException(Throwable t, Object... args);
/**
* <p>斷言對(duì)象<code>obj</code>非空。如果對(duì)象<code>obj</code>為空,則拋出異常
*
* @param obj 待判斷對(duì)象
*/
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
}
/**
* <p>斷言對(duì)象<code>obj</code>非空。如果對(duì)象<code>obj</code>為空,則拋出異常
* <p>異常信息<code>message</code>支持傳遞參數(shù)方式,避免在判斷之前進(jìn)行字符串拼接操作
*
* @param obj 待判斷對(duì)象
* @param args message占位符對(duì)應(yīng)的參數(shù)列表
*/
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;
}
在業(yè)務(wù)中我們可以通過(guò)這個(gè)方式直接拋出枚舉異常。這樣代碼就簡(jiǎn)潔干凈很多
代理已經(jīng)更新到 github倉(cāng)庫(kù)腳手架項(xiàng)目文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-481025.html
關(guān)注公眾號(hào),程序員三時(shí) 持續(xù)輸出優(yōu)質(zhì)內(nèi)容 希望給你帶來(lái)一點(diǎn)啟發(fā)和幫助文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-481025.html
到了這里,關(guān)于SpringBoot定義優(yōu)雅全局統(tǒng)一Restful API 響應(yīng)框架六的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!