Swagger異常定位紀(jì)實Swagger設(shè)計問題分析
前言
swagger ui是一個采用注解驅(qū)動的接口文檔工具,目前已支持標(biāo)準(zhǔn)的open api v3規(guī)范協(xié)議,所以不僅可以在java項目里使用,每個語言都有相應(yīng)的open api實現(xiàn)。項目集成swagger后,可以生成導(dǎo)出open api v3格式化的元數(shù)據(jù)集,有了這個接口元數(shù)據(jù),你可以在任何支持v3協(xié)議的ui上展示你的api信息。在前后端分離的項目中,swagger ui的出現(xiàn),大大提高了前后端聯(lián)調(diào)的效率。
swagger ui在解析注解標(biāo)注的元數(shù)據(jù)信息時,特別場景下會拋異常,而且拋的異常沒有直觀的有價值的異常信息,所以深入的debug了一番,雖然最后問題解決很簡單,但是過程非常曲折。故將bug定位過程記錄在此。
- 影響的Swagger版本:1.5.x
- Swagger core:https://github.com/swagger-api/swagger-core
異常信息
這個異常只會在加載swagger-ui的頁面時會拋出,每次刷新頁面,獲取一次api接口就會觸發(fā)一次異常。
異常分析
@JsonProperty("x-example") public Object getExample() { if (example == null) { return null; } try { if (BaseIntegerProperty.TYPE.equals(type)) { return Long.valueOf(example); } else if (DecimalProperty.TYPE.equals(type)) { return Double.valueOf(example); } else if (BooleanProperty.TYPE.equals(type)) { if ("true".equalsIgnoreCase(example) || "false".equalsIgnoreCase(defaultValue)) { return Boolean.valueOf(example); } } } catch (NumberFormatException e) { LOGGER.warn(String.format("Illegal DefaultValue %s for parameter type %s", defaultValue, type), e); } return example; }
如上是異常相關(guān)的代碼。從異常信息表象來看,是一個強轉(zhuǎn)導(dǎo)致的問題,代碼試圖將一個空的字符串轉(zhuǎn)換成數(shù)值類型導(dǎo)致異常拋出。并且是getExample時拋出的異常,這里需要了解swagger ui的加載過程和基礎(chǔ)架構(gòu)才能直接定位。swagger中的example是為了在生成的api doc中,給出相關(guān)字段的調(diào)用示例,并在觸發(fā)接口調(diào)用時,默認(rèn)自動填充example的值。這里顯然是哪個地方的example設(shè)置不合理導(dǎo)致的異常。那么,接下來要做的就是找到這個空字符串的原始代碼。
DEBUG找到真實原因
借助IDEA的debug功能,點擊異常后面的create breakpoint,在觸發(fā)異常的地方打上斷點。觸發(fā)異常,進入斷點,獲取到了關(guān)鍵信息
一個被描述為app id的字段,用這個信息全局搜索,得到如下的結(jié)果:
有三個相關(guān)的Model實體,首先,這三個Model的appId字段都沒有設(shè)置過example屬性,所以,到這一步,可以先下一個小的結(jié)論,不是我們設(shè)置的example導(dǎo)致的問題,默認(rèn)在不設(shè)置的情況下,example的默認(rèn)值就是空字符串。然后肯定只有其中一個有問題,因為異常只會觸發(fā)一次。在不知道結(jié)果情況下,依次對這三個Model的appId字段加上正確的example描述,經(jīng)測試,只有GetAppBannerRequestDTO加上時,異常才消失,罪魁禍?zhǔn)拙褪撬?。但是,為什么呢?其他兩個Model為啥就沒有問題呢?在博主交叉測驗后,發(fā)現(xiàn)了最終的原因。
結(jié)論及注意事項
當(dāng)Model作用于請求的接收參數(shù)時,并且請求的類型為GET,那么Swagger Ui會自動收集Model所有屬性的examole參數(shù),因為這個參數(shù)是字符串類型,所以會做一個類型轉(zhuǎn)換動作。當(dāng)字段類型為數(shù)值類型,又有沒手動設(shè)置example的值,那么Swagger框架拿到的是個空字符串,強轉(zhuǎn)空字符串就拋異常了。而如果請求是POST,就不會觸發(fā)這段邏輯,所以同為攜帶數(shù)值類型DTO的ImgReplaceRequestDTO沒有問題。如果不是接收參數(shù),作為響應(yīng)參數(shù),也不會觸發(fā)這段邏輯,故而AppBannerResponseVO也就沒有問題了。所以,需要注意的就是當(dāng)DTO作用于GET請求的接收參數(shù)時,切記給所有的數(shù)值類型加上正確的example屬性
后記
博主認(rèn)為這里屬于一個設(shè)計缺陷,而不是我們的使用問題。在獲取example的邏輯里,第一段代碼就判斷了example是否為null。這表明了example有可能為空,但是默認(rèn)值卻設(shè)置了一個空字符串。代表不手動將example設(shè)置為null,這段判null返回的邏輯就永遠(yuǎn)跑不到,而且沒人會這么做,手動給example設(shè)置為null。況且,在觸發(fā)異常的這種場景下,框架不能強制使用者設(shè)置example這種操作。在github倉庫追蹤這塊代碼發(fā)現(xiàn),目前Swagger ui已經(jīng)邁入了3.x版本,全面基于open api v3協(xié)議規(guī)范設(shè)計。所以,這部分代碼完全不一樣了。而存檔的1.5x版本這個問題依舊。
下面是3.x的處理方式,雖然example的默認(rèn)值還是“”。但是通過NotBlank判斷了下,所以不會觸發(fā)異常了
為啥不直接升級3.X?
3.x版本既然已經(jīng)修復(fù)了,為啥不直接升級到3.x版本呢?可能有人會有這個疑問。Swagger3.x版本屬于一個大跨度的迭代版本,和之前的版本完全不兼容,3.x主要面向了open api v3規(guī)范協(xié)議設(shè)計實現(xiàn),注解實體等模型都是一一對應(yīng)的。而在這個版本之前的1.5x系列版本是Swagger自己設(shè)計的api模型。所以代碼層上面完全不兼容,升級的工作量會非常大。不過,新項目還是推薦使用3.x版本,這個版本的api數(shù)據(jù)更通用??梢愿鶕?jù)api的數(shù)據(jù)生成各種語言的客戶端包。就像proto生成客戶端包一樣。
以上就是Swagger異常定位紀(jì)實Swagger設(shè)計問題分析的詳細(xì)內(nèi)容,更多關(guān)于Swagger異常定位Swagger設(shè)計的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于java SSM springboot實現(xiàn)景區(qū)行李寄存管理系統(tǒng)
這篇文章主要介紹了基于java SSM springboot實現(xiàn)的景區(qū)行李寄存管理系統(tǒng),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08

java Class文件內(nèi)部結(jié)構(gòu)解析過程詳解

IDEA “Cannot resolve symbol”爆紅問題解決

springmvc接收json串,轉(zhuǎn)換為實體類List方法

基于SpringBoot服務(wù)端表單數(shù)據(jù)校驗的實現(xiàn)方式

Spring boot如何快速的配置多個Redis數(shù)據(jù)源