簡介
Graceful Response是一個Spring Boot技術棧下的優雅響應處理器,提供一站式統一返回值封裝、全域性異常處理、自定義異常錯誤碼等功能,使用Graceful Response進行web介面開發不僅可以節省大量的時間,還可以提高程式碼質量,使程式碼邏輯更清晰。
強烈推薦你花3分鐘學會它!
本專案案例工程程式碼:https://github.com/feiniaojin/graceful-response-example.git ,注意選擇最新版本的分支。
Spring Boot版本
Graceful Response版本 | graceful-response-example分支 | |
|
|
|
|
|
|
Spring Boot介面開發現狀
目前,業界使用Spring Boot進行介面開發時,往往存在效率底下、重複勞動、可讀性差等問題。以下虛擬碼相信大家非常熟悉,我們大部分專案的Controller介面都是這樣的。
@Controller public class Controller { @GetMapping("/query") @ResponseBody public Response query(Map<String, Object> paramMap) { Response res = new Response(); try { //1.校驗params引數合法性,包括非空校驗、長度校驗等 if (illegal(paramMap)) { res.setCode(1); res.setMsg("error"); return res; } //2.呼叫Service的一系列操作,得到查詢結果 Object data = service.query(params); //3.將操作結果設定到res物件中 res.setData(data); res.setCode(0); res.setMsg("ok"); return res; } catch (Exception e) { //4.異常處理:一堆醜陋的try...catch,如果有錯誤碼的,還需要手工填充錯誤碼 res.setCode(1); res.setMsg("error"); return res; } } }
這段虛擬碼存在什麼樣的問題呢?
第一個問題,效率低下。 Controller層的程式碼應該儘量簡潔,上面的虛擬碼其實只是爲了將資料查詢的結果進行封裝,使其以統一的格式進行返回。例如以下格式的響應體:
{ "code": 0, "msg": "ok", "data": { "id": 1, "name": "username" } }
查詢過程中如果發生異常,需要在Controller進行手工捕獲,根據捕獲的異常人工地設定錯誤碼,當然,也用同樣的格式封裝錯誤碼進行返回。
可以看到,除了呼叫service層的query方法這一行,其他大部分的程式碼都執行進行結果的封裝,大量的冗餘、低價值的程式碼導致我們的開發活動效率很低。
第二個問題,重複勞動。 以上捕獲異常、封裝執行結果的操作,每個介面都會進行一次,因此造成大量重複勞動。
第三個問題,可讀性低。 上面的核心程式碼被淹沒在許多冗餘程式碼中,很難閱讀,如同大海撈針。
我們可以透過Graceful Response這個元件解決這樣的問題。
快速入門
引入Graceful Response元件
Graceful Response已釋出至maven中央倉庫,我們可以直接引入到專案中。
maven依賴如下:
<dependency> <groupId>com.feiniaojin</groupId> <artifactId>graceful-response</artifactId> <version>{latest.version}</version> </dependency>
Spring Boot版本
Graceful Response最新版本 | |
|
|
|
|
啟用Graceful Response
在啟動類中引入@EnableGracefulResponse註解,即可啟用Graceful Response元件。
@EnableGracefulResponse @SpringBootApplication public class ExampleApplication { public static void main(String[] args) { SpringApplication.run(ExampleApplication.class, args); } }
Controller層
引入Graceful Response後,我們不需要再手工進行查詢結果的封裝,直接返回實際結果即可,Graceful Response會自動完成封裝的操作。
Controller層示例如下。
@Controller public class Controller { @RequestMapping("/get") @ResponseBody public UserInfoView get(Long id) { log.info("id={}", id); return UserInfoView.builder().id(id).name("name" + id).build(); } }
在示例程式碼中,Controller層的方法直接返回了UserInfoView物件,沒有進行封裝的操作,但經過Graceful Response處理後,我們還是得到了以下的響應結果。
{ "status": { "code": "0", "msg": "ok" }, "payload": { "id": 1, "name": "name1" } }
而對於命令操作(Command)儘量不返回資料,因此command操作的方法的返回值應該是void,Graceful Response對於對於返回值型別void的方法,也會自動進行封裝。
public class Controller { @RequestMapping("/command") @ResponseBody public void command() { //業務操作 } }
成功呼叫該介面,將得到:
{ "status": { "code": "200", "msg": "success" }, "payload": {} }
Service層
在引入Graceful Response前,有的開發者在定義Service層的方法時,爲了在介面中返回異常碼,乾脆直接將Service層方法定義為Response,淹沒了方法的正常返回值。
Response的程式碼如下。
//lombok註解
@Data public class Response { private String code; private String msg; private Object data; }
直接返回Response的Service層方法:
/** * 直接返回Reponse的Service * 不規範 */ public interface Service { public Reponse commandMethod(Command command); }
Graceful Response引入@ExceptionMapper註解,透過該註解將異常和錯誤碼關聯起來,這樣Service方法就不需要再維護Response的響應碼了,直接丟擲業務異常,由Graceful Response進行異常和響應碼的關聯。
@ExceptionMapper的用法如下。
/** * NotFoundException的定義,使用@ExceptionMapper註解修飾 * code:代表介面的異常碼 * msg:代表介面的異常提示 */ @ExceptionMapper(code = "1404", msg = "找不到物件") public class NotFoundException extends RuntimeException { }
Service介面定義:
public interface QueryService { UserInfoView queryOne(Query query); }
Service介面實現:
public class QueryServiceImpl implements QueryService { @Resource private UserInfoMapper mapper; public UserInfoView queryOne(Query query) { UserInfo userInfo = mapper.findOne(query.getId()); if (Objects.isNull(userInfo)) { //這裏直接拋自定義異常 throw new NotFoundException(); } //……後續業務操作 } }
當Service層的queryOne方法丟擲NotFoundException時,Graceful Response會進行異常捕獲,並將NotFoundException對應的異常碼和異常資訊封裝到統一的響應物件中,最終介面返回以下JSON。
{ "status": { "code": "1404", "msg": "找不到物件" }, "payload": {} }
引數校驗
Graceful Response對JSR-303資料校驗規範和Hibernate Validator進行了增強,Graceful Response自身不提供引數校驗的功能,但是使用者使用了Hibernate Validator後,Graceful Response可以透過@ValidationStatusCode註解為引數校驗結果提供響應碼,並將其統一封裝返回。
例如以下的UserInfoQuery。
@Data public class UserInfoQuery { @NotNull(message = "userName is null !") @Length(min = 6, max = 12) @ValidationStatusCode(code = "520") private String userName; }
UserInfoQuery物件中定義了@NotNull和@Length兩個校驗規則,在未引入Graceful Response的情況下,會直接丟擲異常;
在引入Graceful Response但是沒有加入@ValidationStatusCode註解的情況下,會以預設的錯誤碼進行返回;
在上面的UserInfoQuery中由於使用了@ValidationStatusCode註解,並指定異常碼為520,則當userName欄位任意校驗不透過時,都會使用異常碼520進行返回,如下。
{ "status": { "code": "520", "msg": "userName is null !" }, "payload": {} }
而對於Controller層直接校驗方法入參的場景,Graceful Response也進行了增強,如以下Controller。
public class Controller { @RequestMapping("/validateMethodParam") @ResponseBody @ValidationStatusCode(code = "1314") public void validateMethodParam( @NotNull(message = "userId不能為空") Long userId, @NotNull(message = "userName不能為空") Long userName) { //省略業務邏輯 } }
如果該方法入參校驗觸發了userId和userName的校驗異常,將以錯誤碼1314進行返回,如下。
{ "status": { "code": "1314", "msg": "userId不能為空" }, "payload": {} }
自定義Response格式
Graceful Response內建了兩種風格的響應格式,並透過graceful-response.response-style進行配置。
graceful-response.response-style=0,或者不配置(預設情況),將以以下的格式進行返回:
{ "status": { "code": 1007, "msg": "有內鬼,終止交易" }, "payload": { } }
graceful-response.response-style=1,將以以下的格式進行返回:
{ "code": "1404", "msg": "not found", "data": { } }
如果這兩種格式均不滿足業務需要,Graceful Response也支援使用者自定義響應體,關於自定義響應體的技術實現,請到自定義Response格式進行了解。
本專案提供的進階功能,包括
第三方元件汽車(Swagger、執行器等)
自定義響應
異常請求放行
異常別名
常用配置項