接口服務(wù)主要由兩部分組成,即參數(shù)(輸入)部分,響應(yīng)(輸出)部分。其中在SpringBoot中主要是Controller層作為API的開發(fā)處,其實(shí)在架構(gòu)層面來講,Controller本身是一個(gè)最高的應(yīng)用層,它的職責(zé)是調(diào)用、組裝下層的interface服務(wù)數(shù)據(jù),核心是組裝和調(diào)用,不應(yīng)該摻雜其他相關(guān)的邏輯
博主最近在做一個(gè)數(shù)據(jù)服務(wù)的項(xiàng)目,而這個(gè)數(shù)據(jù)服務(wù)的核心就是對(duì)外暴露的API,值得高興的這是一個(gè)從0開始的項(xiàng)目,所以終于不用受制于“某些歷史”因素去續(xù)寫各種風(fēng)格的Controller,可以在項(xiàng)目伊始就以規(guī)范的技術(shù)和統(tǒng)一形式去搭建API。借此機(jī)會(huì),梳理和匯總一下基于SpringBoot項(xiàng)目開發(fā)REST API的技術(shù)點(diǎn)和規(guī)范點(diǎn)。
接口服務(wù)主要由兩部分組成,即參數(shù)(輸入)部分,響應(yīng)(輸出)部分。其中在SpringBoot中主要是Controller層作為API的開發(fā)處,其實(shí)在架構(gòu)層面來講, Controller 本身是一個(gè)最高的應(yīng)用層,它的職責(zé)是調(diào)用、組裝下層的interface服務(wù)數(shù)據(jù),核心是組裝和調(diào)用,不應(yīng)該摻雜其他相關(guān)的邏輯。
但是往往很多項(xiàng)目里針對(duì)Controller部分的代碼都是十分混亂,有的 Controller 兼顧各種if else的參數(shù)校驗(yàn),有的甚至直接在Controller進(jìn)行業(yè)務(wù)代碼編寫;對(duì)于 Controller 的輸出,有的粗略的加個(gè)外包裝,有的甚至直接把service層的結(jié)構(gòu)直接丟出去;對(duì)于異常的處理也是各種各樣。
以上對(duì)于 Controller 相關(guān)的問題,這里統(tǒng)一用一系列 Controller 的封裝處理來提供優(yōu)化思路。優(yōu)雅且規(guī)范的開發(fā)REST API需要做以下幾步:
直接來看@RestController源碼
@RestController注解等價(jià)于@Controller和@@ResponseBody,@ResponseBody注解的作用是告訴Spring MVC框架,該方法的返回值應(yīng)該直接寫入HTTP響應(yīng)體中,而不是返回一個(gè)視圖(View)。當(dāng)一個(gè)控制器方法被標(biāo)記為
@ResponseBody
時(shí),Spring MVC會(huì)將方法的返回值序列化成JSON或XML等格式,然后發(fā)送給客戶端。更適用于REST API的構(gòu)建。
所以針對(duì)Controller接口的開發(fā), 直接使用@RestController為好。它會(huì)自動(dòng)將Controller下的方法返回內(nèi)容轉(zhuǎn)為REST API的形式 。
例如:
對(duì)于API來講,一般是對(duì)外服務(wù)的基礎(chǔ),不能隨意變更,但是隨著需求和業(yè)務(wù)不斷變化,接口和參數(shù)也會(huì)發(fā)生相應(yīng)的變化。此時(shí)盡可能保證“開閉原則”,以新增接口或增強(qiáng)接口功能來支撐,此時(shí)就需要對(duì)API的版本進(jìn)行維護(hù),以版本號(hào)來確定同一接口的不同能力,一般版本都基于url來控制
例如:
http://localhost:8080/dataserver/v1/queryAccount
http://localhost:8080/dataserver/v2/queryAccount:相比v1版本增強(qiáng)了參數(shù)查詢的靈活性
進(jìn)行API版本控制主要分三步:
該注解可直接使用在Controller類上
首先定義一個(gè)條件匹配類,對(duì)應(yīng)解析Url中的version與ApiVersion注解
這里補(bǔ)充一下 RequestCondition
相關(guān)概念: 它是 Spring 框架中用于請(qǐng)求映射處理的一部分。在 Spring MVC 中,
RequestCondition
接口允許開發(fā)者定義自定義的請(qǐng)求匹配邏輯,這可以基于請(qǐng)求的任何屬性,例如路徑、參數(shù)、HTTP 方法、頭部等。相關(guān)的應(yīng)用場景包括:
路徑匹配(Path Matching) :使用
PatternsRequestCondition
來定義請(qǐng)求的路徑模式,支持 Ant 風(fēng)格的路徑模式匹配,如/api/*
可以匹配所有/api
開頭的請(qǐng)求路徑 。請(qǐng)求方法匹配(Request Method Matching) :通過
RequestMethodsRequestCondition
來限制請(qǐng)求的 HTTP 方法,例如只允許 GET 或 POST 請(qǐng)求 。請(qǐng)求參數(shù)匹配(Request Params Matching) :使用
ParamsRequestCondition
來定義請(qǐng)求必須包含的參數(shù),例如某些接口可能需要特定的查詢參數(shù)才能訪問 。請(qǐng)求頭匹配(Request Headers Matching) :
HeadersRequestCondition
允許定義請(qǐng)求頭的條件,例如某些接口可能需要特定的認(rèn)證頭部才能訪問 。消費(fèi)媒體類型匹配(Consumes Media Type Matching) :
ConsumesRequestCondition
用來定義控制器方法能夠處理的請(qǐng)求體媒體類型,通常用于 RESTful API 中,例如只處理application/json
類型的請(qǐng)求體 。產(chǎn)生媒體類型匹配(Produces Media Type Matching) :
ProducesRequestCondition
定義了控制器方法能夠返回的媒體類型,這通常與Accept
請(qǐng)求頭結(jié)合使用以確定響應(yīng)的格式 。自定義條件匹配 :開發(fā)者可以通過實(shí)現(xiàn)
RequestCondition
接口來定義自己的匹配邏輯,例如根據(jù)請(qǐng)求中的版本號(hào)來路由到不同版本的 API,實(shí)現(xiàn) API 的版本控制 。組合條件匹配(Composite Conditions Matching) :在某些情況下,可能需要根據(jù)多個(gè)條件來匹配請(qǐng)求,
CompositeRequestCondition
可以將多個(gè)RequestCondition
組合成一個(gè)條件來進(jìn)行匹配 。請(qǐng)求映射的優(yōu)先級(jí)選擇(Priority Selection for Request Mapping) :當(dāng)存在多個(gè)匹配的處理器方法時(shí),
RequestCondition
的compareTo
方法用于確定哪個(gè)條件具有更高的優(yōu)先級(jí),以選擇最合適的處理器方法 。
創(chuàng)建一個(gè)版本映射處理器,使用
ApiVersionCondition
作為自定義條件來處理請(qǐng)求映射。當(dāng) Spring MVC 處理請(qǐng)求時(shí),它會(huì)使用這個(gè)自定義的映射處理器來確定哪個(gè)版本的 API 應(yīng)該處理請(qǐng)求。
將上述的處理器注冊(cè)到SpringMvc的處理流程中
驗(yàn)證:
針對(duì)test接口進(jìn)行不同版本的請(qǐng)求:
針對(duì)Account擴(kuò)展版本調(diào)用上一版本接口
當(dāng)請(qǐng)求對(duì)應(yīng)的版本不存在接口時(shí),會(huì)匹配之前版本的接口,即請(qǐng)求
/v2/account/extend
接口時(shí),由于v2 控制器未實(shí)現(xiàn)該接口,所以自動(dòng)匹配v1 版本中的接口。這就實(shí)現(xiàn)了
API版本繼承
。
@Validated
是一個(gè)用于 Java 應(yīng)用程序中的注解,特別是在 Spring 框架中,以指示目標(biāo)對(duì)象或方法需要進(jìn)行驗(yàn)證。這個(gè)注解通常與 JSR 303/JSR 380 規(guī)范的 Bean Validation API 結(jié)合使用,以確保數(shù)據(jù)的合法性和完整性。
@Validated注解的三種用法:
方法級(jí)別驗(yàn)證
:當(dāng)
@Validated
注解用在方法上時(shí),它指示 Spring 在調(diào)用該方法之前執(zhí)行參數(shù)的驗(yàn)證。如果參數(shù)不符合指定的驗(yàn)證條件,將拋出
MethodArgumentNotValidException
。
類級(jí)別驗(yàn)證
:將
@Validated
注解用在類上,表示該類的所有處理請(qǐng)求的方法都會(huì)進(jìn)行驗(yàn)證。這可以減少在每個(gè)方法上重復(fù)注解的需要。
組合注解
:Spring 還提供了
@Valid
注解,它是
@Validated
的一個(gè)更簡單的形式,只觸發(fā)驗(yàn)證并不指定特定的驗(yàn)證組(Validation Groups)。
@Validated
允許你指定一個(gè)或多個(gè)驗(yàn)證組,這在需要根據(jù)不同情況執(zhí)行不同驗(yàn)證規(guī)則時(shí)非常有用。
在REST API中進(jìn)行參數(shù)驗(yàn)證一般使用方法級(jí)別驗(yàn)證即可,即對(duì)參數(shù)Dto的類內(nèi)信息進(jìn)行驗(yàn)證,例如一個(gè)分頁的查詢參數(shù)類:
在Controller中配合@Validated使用:
此時(shí)如果前端傳入?yún)?shù)不合法,例如pageNo為0又或者productType不存在,則會(huì)拋出
MethodArgumentNotValidException
的異常。稍后對(duì)于異常進(jìn)行處理即可完成參數(shù)的驗(yàn)證。
這里的@Max
、
@Min
和
@NotNull
注解屬于 Bean Validation API 的一部分,這是一個(gè) JSR 303/JSR 380 規(guī)范,用于在 Java 應(yīng)用程序中提供聲明式驗(yàn)證功能。這些注解用于約束字段值的范圍和非空性。類似的注解還有:
注解 | 作用 |
@NotNull |
驗(yàn)證注解的字段值不能為
null
。
|
@NotEmpty |
與
@NotNull
類似,但用于集合或字符串,驗(yàn)證注解的字段值不能為
null
,且對(duì)于字符串,長度不能為 0。
|
@NotBlank |
驗(yàn)證注解的字段值不能為
null
,且不能是空白字符串(空白包括空格、制表符等)。
|
@Min(value) |
驗(yàn)證注解的字段值是否大于或等于指定的最小值。
value
參數(shù)接受一個(gè)整數(shù)。
|
@Max(value) |
驗(yàn)證注解的字段值是否小于或等于指定的最大值。
value
參數(shù)接受一個(gè)整數(shù)。
|
@Size(min, max) | 驗(yàn)證字符串或集合的大小在指定的最小值和最大值之間。 |
@Pattern(regex) | 驗(yàn)證字段值是否符合指定的正則表達(dá)式。 |
注:SpringBoot 2.3.1 版本默認(rèn)移除了校驗(yàn)功能,如果想要開啟的話需要添加以上依賴
@RestControllerAdvice
是 @ResponseBody+@ControllerAdvice的集合注解,用于定義一個(gè)控制器級(jí)別的異常處理類。一般用來進(jìn)行全局異常處理,在@RestControllerAdvice
類中處理異常后,可以直接返回一個(gè)對(duì)象,該對(duì)象會(huì)被轉(zhuǎn)換為 JSON 或 XML 響應(yīng)體,返回給客戶端。
在
使用@Validated和 Bean Validation API 的注解進(jìn)行參數(shù)校驗(yàn)后,當(dāng)出現(xiàn)不符合規(guī)定的參數(shù)會(huì)拋出
MethodArgumentNotValidException
異常
,這里就可以使用@RestControllerAdvice注解來創(chuàng)建一個(gè)全局Controller異常攔截類,來統(tǒng)一處理各類異常
這里只以
MethodArgumentNotValidException
異常進(jìn)行攔截,在
@RestControllerAdvice類內(nèi)可以創(chuàng)建多個(gè)方法,通過@ExceptionHandler對(duì)不同的異常進(jìn)行定制化處理,這樣當(dāng)Controller內(nèi)發(fā)生異常,都可以在@RestControllerAdvice類內(nèi)進(jìn)行截獲、處理、返回給客戶端安全的信息。
首先,進(jìn)行統(tǒng)一的響應(yīng)格式,這里需要封裝一個(gè)固定返回格式的結(jié)構(gòu)對(duì)象: ResponseData
其中對(duì)于相關(guān)的狀態(tài)碼最好進(jìn)行統(tǒng)一的封裝,便于以后的開發(fā),創(chuàng)建狀態(tài)枚舉:
將上述的 ResponseData 中狀態(tài)類相關(guān)替換為枚舉
這樣Controller的接口統(tǒng)一返回格式就是標(biāo)準(zhǔn)的結(jié)構(gòu)了。
有了統(tǒng)一響應(yīng)體的Controller在返回時(shí)可以這樣寫:
即便如此,團(tuán)隊(duì)開發(fā)中可能還會(huì)出現(xiàn)換個(gè)人新寫Controller不知道有統(tǒng)一返回體這回事,為了更保險(xiǎn),可以通過AOP進(jìn)行統(tǒng)一對(duì)結(jié)果進(jìn)行封裝,不論Controller返回啥,到客戶端的數(shù)據(jù)都包含一個(gè)包裝體。
具體實(shí)現(xiàn)是使用 @RestControllerAdvice類 實(shí)現(xiàn) ResponseBodyAdvice 接口來完成。
我們以test接口為例,test接口原本返回的是String,而toint返回的是Integer
但是頁面返回是JSON字符串和返回體:
接口開發(fā)完成,調(diào)試時(shí),大多數(shù)都是使用Postman模擬請(qǐng)求調(diào)試或者直接用前端代碼調(diào)用調(diào)試,其實(shí)這兩種都比較麻煩,尤其面對(duì)復(fù)制參數(shù)時(shí),Postman要逐個(gè)接口的錄入,十分費(fèi)事,其實(shí)這里可以在SpringBoot中引入Swagger接口文檔組件,接口文檔和接口調(diào)試一并解決。
直接在maven中引入相關(guān)依賴:
標(biāo)準(zhǔn)的swagger3引入以上兩個(gè)依賴即可,相關(guān)版本可自行選擇
下面進(jìn)行swagger的配置類和一些swagger相關(guān)頁面的配置
Swagger相關(guān)注解明細(xì)
注解 | 使用位置 | 作用 |
@Api | 作用在類上, Controller 類 |
表示對(duì)類的說明,通常用于描述 Controller 的作用和分類,如
@Api(tags = "用戶管理"),后續(xù)會(huì)在Swagger文檔中以
目錄形式
展示
|
@ApiOperation | 作用在方法上,一般為 Controller中具體方法 |
描述 API 接口的具體操作和功能,例如
@ApiOperation(value = "獲取用戶列表", notes = "根據(jù)條件獲取用戶列表")
,在swagger文檔中以目錄內(nèi)容體現(xiàn)
|
@ApiModel | 作用于類上,一般是 參數(shù)實(shí)體類 |
表示這是一個(gè)模型類,通常與
@ApiModelProperty
結(jié)合使用來描述模型屬性 。
|
@ApiModelProperty | 用于模型類的屬性上, 參數(shù)類的成員變量 |
描述屬性的信息,如
@ApiModelProperty(value = "用戶名", required = true)
|
@ApiImplicitParams 和 @ApiImplicitParam | 用于方法上,一般為 Controller中具體方法 | 描述接口的隱含參數(shù),例如請(qǐng)求參數(shù)或請(qǐng)求頭信息 |
@ApiResponses 和 @ApiResponse | 用于方法上,一般為 Controller中具體方法 | 描述接口的響應(yīng)信息,可以指定不同的響應(yīng)狀態(tài)碼和對(duì)應(yīng)的描述信息 。 |
@ApiIgnore | 用于類或方法上 | 表示忽略該類或方法,不將其顯示在Swagger文檔中。 |
@Api 和 @ApiOperation 使用
@ApiMode 和 @ApiModelProperty
@ApiImplicitParams 和 @ApiImplicitParam
與ApiMode和ApiModeProperty功能一致,一般用于get請(qǐng)求中的參數(shù)描述
使用swagger后,直接在頁面訪問 http://127.0.0.1:8080/XXX/doc.html即可訪問接口頁面
不要復(fù)雜的postman調(diào)用,本地調(diào)試可以直接使用調(diào)試功能
關(guān)于參數(shù)驗(yàn)證的異常處理和統(tǒng)一返回結(jié)構(gòu),可以使用一個(gè)類來完成,以下是完整模板:
golang slice相關(guān)常見的性能優(yōu)化手段
閱讀連接Elasticsearch服務(wù)器的Python代碼示例
閱讀國產(chǎn)操作系統(tǒng)上實(shí)現(xiàn)RTMP推流攝像頭視頻和麥克風(fēng)聲音到流媒體服務(wù)器
閱讀使用Python讀取和導(dǎo)出NetCDF格式的多時(shí)相柵格文件
閱讀多租戶系統(tǒng)數(shù)據(jù)權(quán)限設(shè)計(jì)與RuoYi系統(tǒng)的借鑒
閱讀count(*)、count(1)哪個(gè)更快?面試必問:通宵整理的十道經(jīng)典MySQL必問面試題
閱讀從需求分析、產(chǎn)品設(shè)計(jì)到部署交付各階段說明
閱讀如何利用七牛云進(jìn)行數(shù)據(jù)備份和刪除
閱讀強(qiáng)化學(xué)習(xí)筆記之【ACE:Off-PolicyActor-CriticwithCausality-AwareEntropyRegularization】
閱讀使用MailKit在.NET Core中收發(fā)郵件的完整示例
閱讀WiFi基礎(chǔ)(六):天線基礎(chǔ)知識(shí)
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請(qǐng)發(fā)郵件[email protected]
湘ICP備2022002427號(hào)-10 湘公網(wǎng)安備:43070202000427號(hào)© 2013~2024 haote.com 好特網(wǎng)