衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一站式統一返回值封裝、異常處理、異常錯誤碼解決方案—最強的Sping Boot接口優雅響應處理器

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-06-20 15:42 ? 次閱讀

1. 前言

統一返回值封裝、統一異常處理和異常錯誤碼體系的意義在于提高代碼的可維護性和可讀性,使得代碼更加健壯和穩定。統一返回值封裝可以避免每一個接口都需要手工拼裝響應報文;統一異常處理可以將異常處理的邏輯集中到一個地方,避免代碼中出現大量的try-catch語句,降低了代碼的復雜度,提高了代碼的可讀性;異常體系的設計可以清晰地區分不同類型的異常,使得開發者能夠更加精準地處理異常情況,并且能夠更好地定位和解決問題。

Graceful Response是一個Spring Boot體系下的優雅響應處理組件,提供一站式統一返回值封裝、全局異常處理、自定義異常錯誤碼、自定義參數校驗異常碼等功能,使用Graceful Response進行web接口開發不僅可以節省大量的時間,還可以提高代碼質量,使代碼邏輯更清晰。

強烈推薦你花3分鐘學會它!

Graceful Response的Github地址: https://github.com/feiniaojin/graceful-response ,歡迎star!

Graceful Response的案例工程代碼:https://github.com/feiniaojin/graceful-response-example.git

2. Spring Boot Web API接口數據返回的現狀

我們進行Spring Boo Web API接口開發時,通常大部分的Controller代碼是這樣的:

public class Controller {
    @GetMapping("/query")
    @ResponseBody
    public Response query(Parameter params) {

        Response res = new Response();
        try {
            //1.校驗params參數,非空校驗、長度校驗
            if (illegal(params)) {
                res.setCode(1);
                res.setMsg("error");
                return res;
            }
            //2.調用Service的一系列操作
            Data data = service.query(params);
            //3.執行正確時,將操作結果設置到res對象中
            res.setData(data);
            res.setCode(0);
            res.setMsg("ok");
            return res;
        } catch (BizException1 e) {
            //4.異常處理:一堆丑陋的try...catch,如果有錯誤碼的,還需要手工填充錯誤碼
            res.setCode(1024);
            res.setMsg("error");
            return res;
        } catch (BizException2 e) {
            //4.異常處理:一堆丑陋的try...catch,如果有錯誤碼的,還需要手工填充錯誤碼
            res.setCode(2048);
            res.setMsg("error");
            return res;
        } catch (Exception e) {
            //4.異常處理:一堆丑陋的try...catch,如果有錯誤碼的,還需要手工填充錯誤碼
            res.setCode(1);
            res.setMsg("error");
            return res;
        }
    }
}

這段代碼存在什么問題呢?真正的業務邏輯被冗余代碼淹沒,可讀性太差。

真正執行業務的代碼只有

Data data=service.query(params);

其他代碼不管是正常執行還是異常處理,都是為了異常封裝、把結果封裝為特定的格式,例如以下格式:

{
  "code": 0,
  "msg": "ok",
  "data": {
    "id": 1,
    "name": "username"
  }
}

這樣的邏輯每個接口都需要處理一遍,都是繁瑣的重復勞動。

現在,只需要引入Graceful Response組件并通過@EnableGracefulResponse啟用,就可以直接返回業務結果并自動完成response的格式封裝。

以下是使用Graceful Response之后的代碼,實現同樣的返回值封裝、異常處理、異常錯誤碼功能,但可以看到代碼變得非常簡潔,可讀性非常強。

public class Controller {
    @GetMapping("/query")
    @ResponseBody
    public Data query(Parameter params) {
       return service.query(params);
    }
}

3. 快速入門

3.1 引入maven依賴

graceful-response已發布至maven中央倉庫,可以直接引入到項目中,maven依賴如下:


    com.feiniaojin
    graceful-response
    {此處替換為最新的版本號}

以下鏈接可以查看maven中央倉庫中最新的版本:

https://central.sonatype.com/artifact/com.feiniaojin/graceful-response/3.0/versions

3.2 在啟動類中引入@EnableGracefulResponse注解

@EnableGracefulResponse
@SpringBootApplication
public class ExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
}

3.3 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();
    }
}

UserInfoView的源碼:

@Data
@Builder
public class UserInfoView {
    private Long id;
    private String name;
}

這個接口直接返回了 UserInfoView的實例對象,調用接口時,Graceful Response將自動封裝為以下格式:

{
  "status": {
    "code": "0",
    "msg": "ok"
  },
  "payload": {
    "id": 1,
    "name": "name1"
  }
}

可以看到UserInfoView被自動封裝到payload字段中。

Graceful Response提供了兩種風格的Response,可以通過在application.properties文件中配置gr.responseStyle=1,將以以下的格式進行返回:

{
  "code": "0",
  "msg": "ok",
  "data": {
    "id": 1,
    "name": "name1"
  }
}

如果這兩種風格也不能滿足需要,我們還可以根據自己的需要進行自定義返回的Response格式。詳細見本文 4.3自定義Respnse格式。

?異常處理的場景

通過Graceful Response,我們不需要專門在Controller中處理異常,詳細見 4.1 Graceful Response異常錯誤碼處理。

?返回值為空的場景

某些Command類型的方法只執行修改操作,不返回數據,這個時候我們可以直接在Controller中返回void,Graceful Response會自動封裝默認的操作成功Response報文。

@Controller
public class Controller {
    @RequestMapping("/void")
    @ResponseBody
    public void testVoidResponse() {
        //省略業務操作
    }
}

testVoidResponse方法的返回時void,調用這個接口時,將返回:

{
  "status": {
    "code": "200",
    "msg": "success"
  },
  "payload": {}
}

3.4 Service方法業務處理

在引入Graceful Response后,Service層的方法的可讀性可以得到極大的提升。

?接口直接返回業務數據類型,而不是Response,更具備可讀性

public interface ExampleService {
    UserInfoView query1(Query query);
}

?Service接口實現類中,直接拋自定義的業務異常,Graceful Response將其轉化為返回錯誤碼和錯誤提示

public class ExampleServiceImpl implements ExampleService {
    @Resource
    private UserInfoMapper mapper;

    public UserInfoView query1(Query query) {
        UserInfo userInfo = mapper.findOne(query.getId());
        if (Objects.isNull(userInfo)) {
           //這里直接拋自定義異常,異常通過@ExceptionMapper修飾,提供異常碼和異常提示
           throw new NotFoundException();
        }
        // 省略后續業務操作
    }
}
/**
 * NotFoundException的定義,使用@ExceptionMapper注解修飾
 * code:代表接口的異常碼
 * msg:代表接口的異常提示
 */
@ExceptionMapper(code = "1404", msg = "找不到對象")
public class NotFoundException extends RuntimeException {

}
//Controller不再捕獲處理異常
@RequestMapping("/get")
@ResponseBody
public UserInfoView get(Query query)) {
    return exampleService.query1(query);
}

當Service方法拋出NotFoundException異常時,接口將直接返回錯誤碼,不需要手工set,極大地簡化了異常處理邏輯。

{
  "status": {
    "code": "1404",
    "msg": "找不到對象"
  },
  "payload": {}
}

驗證:啟動example工程后,請求

http://localhost:9090/example/notfound

3.5 通用異常類和通用工具類

@ExceptionMapper設計的初衷是將異常與錯誤碼關聯起來,用戶只需要拋異常,不需要再關注異常與錯誤碼的對應關系。

部分用戶反饋,希望在不自定義新異常類的情況下,也能可以按照預期返回錯誤碼和異常信息,因此從2.1版本開始,新增了GracefulResponseException異常類,用戶只需要拋出該異常即可。

public class Service {
  
  public void method() {
    throw new GracefulResponseException("自定義的錯誤碼","自定義的錯誤信息");
  }
}

為簡化使用,從2.1版本開始提供GracefulResponse通用工具類,在需要拋出GracefulResponseException時,只需要調用raiseException方法即可。 這樣設計原因是用戶拋通用異常,其實已經不關心具體是什么異常了,用戶實際上只是想要錯誤碼和錯誤信息。

示例如下:

public class Service {

    public void method() {
        
        //當condition==true時,拋出GracefulResponseException異常,返回自定義的錯誤碼和錯誤信息
        if (condition) {
            GracefulResponse.raiseException("自定義的錯誤碼", "自定義的錯誤信息");
        }
        //省略其他業務邏輯
    }
}

3.6 參數校驗異常以及錯誤碼

3.0版本以前,如果引用validation框架并發生了校驗異常,Graceful Response在默認情況下會捕獲并返回code=1,參數校驗發生的異常信息會丟失;如果使用異常別名功能,可以對大的校驗異常返回統一的錯誤碼,但是不夠靈活且依舊沒有解決參數異常提示的問題。

Graceful Response從3.0版本開始,引入@ValidationStatusCode注解,可以非常方便地支持validation校驗異常。

@ValidationStatusCode注解目前只有一個code屬性,用于指定參數校驗異常時的錯誤碼,錯誤提示則取自validation校驗框架。

對入參類進行參數校驗

@Data
public class UserInfoQuery {

    @NotNull(message = "userName is null !")
    @Length(min = 6, max = 12)
    @ValidationStatusCode(code = "520")
    private String userName;

}

userName字段任意一項校驗不通過時,接口將會返回異常碼520和校驗注解中的message

{
  "status": {
    "code": "520",
    "msg": "userName is null !"
  },
  "payload": {}
}

詳細見example工程ExampleController的validateDto方法
http://localhost:9090/example/validateDto

注意:@ValidationStatusCode校驗參數對象字段的情況,code取值順序為:會先取字段上的注解,再去該屬性所在對象的類(即UserInfoQuery類)上的注解,再取全局配置的參數異常碼gr.defaultValidateErrorCode,最后取默認的全局默認的錯誤碼(默認code=1)

直接在Controller中校驗方法入參

直接在Controller方法中進行參數校驗:

@Validated
public class ExampleController {

  @RequestMapping("/validateMethodParam")
  @ResponseBody
  @ValidationStatusCode(code = "1314")
  public void validateMethodParam(@NotNull(message = "userId不能為空") Long userId,
                                  @NotNull(message = "userName不能為空") Long userName) {
      //省略業務邏輯
  }
}

當userId、或者userName校驗不通過時,將會返回code=1314,msg為對應的校驗信息。

{
  "status": {
    "code": "1314",
    "msg": "userId不能為空"
  },
  "payload": {}
}

詳細見example工程ExampleController的validateMethodParam方法
http://localhost:9090/example/validateMethodParam

注意:@ValidationStatusCode校驗Controller方法參數字段的情況,code取值順序為:會先取當前方法上的注解,再去該方法所在類(即ExampleController類)上的注解,再取全局配置的參數異常碼gr.defaultValidateErrorCode,最后取默認的全局默認的錯誤碼(默認code=1)

4. 進階用法

4.1 Graceful Response異常錯誤碼處理

以下是使用Graceful Response進行異常、錯誤碼處理的開發步驟。

?創建自定義異常

通過繼承RuntimeException類創建自定義的異常,采用 @ExceptionMapper注解修飾,注解的 code屬性為返回碼,msg屬性為錯誤提示信息。

關于是繼承RuntimeException還是繼承Exception,讀者可以根據實際情況去選擇,Graceful Response對兩者都支持。

@ExceptionMapper(code = "1007", msg = "有內鬼,終止交易")
public static final class RatException extends RuntimeException {

}

?Service執行具體邏輯

Service執行業務邏輯的過程中,需要拋異常的時候直接拋出去即可。由于已經通過@ExceptionMapper定義了該異常的錯誤碼,我們不需要再單獨的維護異常碼枚舉與異常類的關系。

//Service層偽代碼
public class Service {
    public void illegalTransaction() {
        //需要拋異常的時候直接拋
        if (check()) {
            throw new RatException();
        }
        doIllegalTransaction();
    }
}

Controller層調用Service層偽代碼:

public class Controller {
    @RequestMapping("/test3")
    public void test3() {
        //Controller中不會進行異常處理,也不會手工set錯誤碼,只關心核心操作,其他的統統交給Graceful Response
        exampleService.illegalTransaction();
    }
}

在瀏覽器中請求controller的/test3方法,有異常時將會返回:

{
  "status": {
    "code": "1007",
    "msg": "有內鬼,終止交易"
  },
  "payload": {
  }
}

4.2 外部異常別名

案例工程( https://github.com/feiniaojin/graceful-response-example.git )啟動后, 通過瀏覽器訪問一個不存在的接口,例如 http://localhost:9090/example/get2?id=1
如果沒開啟Graceful Response,將會跳轉到404頁面,主要原因是應用內部產生了 NoHandlerFoundException異常。如果開啟了Graceful Response,默認會返回code=1的錯誤碼。

這類非自定義的異常,如果需要自定義一個錯誤碼返回,將不得不對每個異常編寫Advice邏輯,在Advice中設置錯誤碼和提示信息,這樣做也非常繁瑣。

Graceful Response可以非常輕松地解決給這類外部異常定義錯誤碼和提示信息的問題。

以下為操作步驟:

?創建異常別名,并用 @ExceptionAliasFor注解修飾

@ExceptionAliasFor(code = "1404", msg = "Not Found", aliasFor = NoHandlerFoundException.class)
public class NotFoundException extends RuntimeException {
}

code:捕獲異常時返回的錯誤碼

msg:異常提示信息

aliasFor:表示將成為哪個異常的別名,通過這個屬性關聯到對應異常。

?注冊異常別名

創建一個繼承了AbstractExceptionAliasRegisterConfig的配置類,在實現的registerAlias方法中進行注冊。

@Configuration
public class GracefulResponseConfig extends AbstractExceptionAliasRegisterConfig {

    @Override
    protected void registerAlias(ExceptionAliasRegister aliasRegister) {
        aliasRegister.doRegisterExceptionAlias(NotFoundException.class);
    }
}

?瀏覽器訪問不存在的URL

再次訪問 http://localhost:9090/example/get2?id=1 ,服務端將返回以下json,正是在ExceptionAliasFor中定義的內容

{
  "code": "1404",
  "msg": "not found",
  "data": {
  }
}

4.3 自定義Response格式

Graceful Response內置了兩種風格的響應格式,可以在application.properties文件中通過gr.responseStyle進行配置。

?gr.responseStyle=0,或者不配置(默認情況)

將以以下的格式進行返回:

{
  "status": {
    "code": "1007",
    "msg": "有內鬼,終止交易"
  },
  "payload": {
  }
}

?gr.responseStyle=1

將以以下的格式進行返回:

{
  "code": "1404",
  "msg": "not found",
  "data": {
  }
}

?自定義響應格式

如果以上兩種格式均不能滿足業務需要,可以通過自定義去滿足,Response

例如以下響應:

public class CustomResponseImpl implements Response {

    private String code;

    private Long timestamp = System.currentTimeMillis();

    private String msg;

    private Object data = Collections.EMPTY_MAP;

    @Override
    public void setStatus(ResponseStatus statusLine) {
        this.code = statusLine.getCode();
        this.msg = statusLine.getMsg();
    }

    @Override
    @JsonIgnore
    public ResponseStatus getStatus() {
        return null;
    }

    @Override
    public void setPayload(Object payload) {
        this.data = payload;
    }

    @Override
    @JsonIgnore
    public Object getPayload() {
        return null;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Long getTimestamp() {
        return timestamp;
    }
}

注意,不需要返回的屬性可以返回null或者加上@JsonIgnore注解

?配置gr.responseClassFullName

將CustomResponseImpl的全限定名配置到gr.responseClassFullName屬性。

gr.responseClassFullName=com.feiniaojin.gracefuresponse.example.config.CustomResponseImpl

注意,配置gr.responseClassFullName后,gr.responseStyle將不再生效。

實際的響應報文如下:

{
    "code":"200",
    "timestamp":1682489591319,
    "msg":"success",
    "data":{

    }
}

如果還是不能滿足需求,那么可以考慮同時自定義實現Response和ResponseFactory這兩個接口。

5. 常用配置

Graceful Response在版本迭代中,根據用戶反饋提供了一些常用的配置項,列舉如下:

gr.printExceptionInGlobalAdvice
是否打印異常日志,默認為false

gr.responseClassFullName
自定義Response類的全限定名,默認為空。 配置gr.responseClassFullName后,gr.responseStyle將不再生效

gr.responseStyle
Response風格,不配置默認為0

gr.defaultSuccessCode
自定義的成功響應碼,不配置則為0

gr.defaultSuccessMsg
自定義的成功提示,默認為ok

gr.defaultErrorCode
自定義的失敗響應碼,默認為1

gr.defaultErrorMsg
自定義的失敗提示,默認為error

gr.defaultValidateErrorCode
全局的參數校驗錯誤碼,默認等于gr.defaultErrorCode

6. 總結

本文介紹了Graceful Response這個框架的使用,讀者在使用過程中遇到問題,歡迎到GitHub提交issue進行反饋,幫助我們將Graceful Response優化得更好。

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 處理器
    +關注

    關注

    68

    文章

    19409

    瀏覽量

    231190
  • 接口
    +關注

    關注

    33

    文章

    8692

    瀏覽量

    151922
  • 封裝
    +關注

    關注

    127

    文章

    7997

    瀏覽量

    143410
收藏 人收藏

    評論

    相關推薦

    高性能BMS AFE打造一站式儲能電池包高壓監測解決方案

    高性能BMS AFE打造一站式儲能電池包高壓監測解決方案
    的頭像 發表于 12-24 09:50 ?214次閱讀
    高性能BMS AFE打造<b class='flag-5'>一站式</b>儲能電池包高壓監測<b class='flag-5'>解決方案</b>

    HTTP相關返回值異常如何解決(上篇)

    協議。它是萬維網(WWW)的基礎,允許客戶端(通常是網頁瀏覽)與服務之間進行通信。以下是對 HTTP 的些基本介紹: 基本概念 請求-響應模型: HTTP 使用請求-
    的頭像 發表于 10-20 16:40 ?435次閱讀
    HTTP相關<b class='flag-5'>返回值</b><b class='flag-5'>異常</b>如何解決(上篇)

    智慧路燈的定義 全方位解析物聯網智慧燈桿一站式解決方案

    智慧路燈的定義(全方位解析物聯網智慧燈桿一站式解決方案
    的頭像 發表于 10-12 09:26 ?853次閱讀
    智慧路燈的定義 全方位解析物聯網智慧燈桿<b class='flag-5'>一站式</b><b class='flag-5'>解決方案</b>

    ARM處理器異常中斷響應過程

    ARM處理器異常中斷響應是嵌入系統設計中個至關重要的環節,它確保了系統在面對內部或外部事件時能夠穩定、可靠地運行。
    的頭像 發表于 09-10 11:18 ?1144次閱讀

    嵌入C編程常用的異常錯誤處理

    嵌入C編程中,異常錯誤處理是確保系統穩定性和可靠性的重要部分。以下是些常見的異常錯誤處理方法
    發表于 08-06 14:32

    光纖處理一站式解決方案:剝除-切割-熔接-涂覆(全品類光纖)

    電子發燒友網站提供《光纖處理一站式解決方案:剝除-切割-熔接-涂覆(全品類光纖).pdf》資料免費下載
    發表于 07-27 13:29 ?0次下載

    EMC與EMI一站式解決方案:從源頭解決,滿足您的需求

    深圳比創達|EMC與EMI一站式解決方案:從源頭解決,滿足您的需求
    的頭像 發表于 06-28 10:21 ?556次閱讀
    EMC與EMI<b class='flag-5'>一站式</b><b class='flag-5'>解決方案</b>:從源頭解決,滿足您的需求

    EMC與EMI一站式解決方案:源頭到終端的全面防護

    深圳比創達電子|EMC與EMI一站式解決方案:源頭到終端的全面防護
    的頭像 發表于 06-21 10:46 ?522次閱讀
    EMC與EMI<b class='flag-5'>一站式</b><b class='flag-5'>解決方案</b>:源頭到終端的全面防護

    EMC與EMI一站式解決方案:電磁兼容與干擾挑戰

    深圳比創達EMC|EMC與EMI一站式解決方案:電磁兼容與干擾挑戰
    的頭像 發表于 06-14 10:15 ?534次閱讀
    EMC與EMI<b class='flag-5'>一站式</b><b class='flag-5'>解決方案</b>:電磁兼容與干擾挑戰

    EMC與EMI一站式解決方案:攻克電磁兼容難題

    深圳比創達電子EMC|EMC與EMI一站式解決方案:攻克電磁兼容難題
    的頭像 發表于 06-05 11:05 ?431次閱讀

    EMC與EMI一站式解決方案:理論到實踐的跨越

    深圳比創達電子EMC|EMC與EMI一站式解決方案:理論到實踐的跨越
    的頭像 發表于 05-24 09:44 ?572次閱讀
    EMC與EMI<b class='flag-5'>一站式</b><b class='flag-5'>解決方案</b>:理論到實踐的跨越

    深圳比創達電子|EMI一站式解決方案:提升企業電磁兼容性的路徑.

    的傳播和擴散。這種技術廣泛應用于電子設備的外殼、線纜和接口等部位,提高設備的抗干擾能力;2、濾波技術濾波技術是EMI一站式解決方案中的另關鍵技術。通過對信號進行濾波
    發表于 05-08 11:51

    EMI一站式解決方案:提升企業電磁兼容性的路徑

    深圳比創達電子|EMI一站式解決方案:提升企業電磁兼容性的路徑
    的頭像 發表于 05-08 11:45 ?589次閱讀
    EMI<b class='flag-5'>一站式</b><b class='flag-5'>解決方案</b>:提升企業電磁兼容性的路徑

    如何處理STM32的HAL庫函數返回異常問題?

    STM32運行死機了,是很可能可以在這種 庫函數運行返回值不是 HAL_OK 中進行故障定位的。 (3)問題是 旦出現了這種返回值異常錯誤
    發表于 04-17 06:39

    機房建設一站式整體解決方案

    紛紛轉向種全面而高效的建設模式----機房建設一站式整體解決方案一站式方案:從規劃到運
    的頭像 發表于 04-02 16:15 ?482次閱讀
    申博百家乐官网下载| 百家乐官网看图赢钱| 缅甸百家乐博彩| 德州扑克刷分| 百家乐官网牌具公司| 百家乐返点| 永利线上娱乐| 财富百家乐官网的玩法技巧和规则| 网上百家乐辅助软件| 香港六合彩开奖历史记录| 百家乐官网必赢法冯耘| 星期8百家乐的玩法技巧和规则 | 澳门百家乐网40125| 百家乐官网高人玩法| 百家乐代理博彩正网| 澳门美高梅| 怎么看百家乐官网走势| 皇冠现金网怎么样| 百家乐官网全部规| 大发888下载 客户端| 百家乐官网7scs娱乐平台| 威尼斯人娱乐骰宝| 百家乐官网电投网址| 百家乐怎么玩才会赢钱| 百家乐官网视频软件| 在线百家乐纸牌游戏| 百家乐官网下注平台| 星级百家乐技巧| 百家乐官网相对策略| 百家乐赌场信息| 百家乐官网赌王有哪些| 网上百家乐破战| 百家乐官网最好的平台是哪个 | 阳宅风水24向详解| 立即博最新网址| 北京百家乐网上投注| 百家乐官网娱乐城返水| 乐天堂百家乐赌场娱乐网规则| 百家乐官网对保| 大发888登录| 百家乐蓝盾假网|