在如今前后端分離開發的模式下,前端調用后端提供的API去實現數據的展示或者相關的數據操作,保證及時更新和完整的REST API文檔將會大大地提高兩邊的工作效率,減少不必要的溝通成本。本文采用的Swagger2就是一個當前流行的通過少量的注解就可以生成漂亮的API文檔工具,且在生成的在線文檔中提供類似POSTMAN直接調試能力,不僅僅是靜態的文檔。接下來將會利用這個工具與Spring Boot項目結合,最終生成我們上一篇文章中所涉及到的REST API文檔。
這一篇文章基本將Swagger2在生產環境中可能會用到的配置都有涉及,慢慢看吧,看了這一篇因該是夠了。
Swagger是與用于實現 OpenAPI 文檔廣泛使用的工具,Swagger工具集包括開源工具,免費工具和商業工具的組合,可在API生命周期的不同階段使用。
Swagger Editor
(開源):使用Swagger編輯器,可以在瀏覽器內的YAML文檔中編輯OpenAPI規范并支持實時預覽文檔,可以參考官方的Demo?https://editor.swagger.io/
Swagger UI
(開源):讓Swagger產生的文檔更漂亮,而且支持API交互操作,在生成文檔后,直接在瀏覽器中瀏覽,并可以實現類似curl
命令或者postman
訪問我們的API,并返回相關數據。
Swagger Codegen
(開源): 是一個代碼生成器,可以通過Swagger API定義生成不同語言版本的服務端和客戶端工程代碼。
Swagger Core
(開源):用于生成Swagger API規范的示例和服務器集成,可輕松訪問REST API,結合Swagger UI
,讓生成的文檔更漂亮。
Swagger Parser
(開源): Java開發,解析OpenAPI定義的獨立庫
Swagger Inspector
(免費):API在線測試工具,驗證API并從現有API生成OpenAPI定義功能?https://goo.gl/fZYHWz
SwaggerHub
(免費和商用版):API設計和文檔化,為使用OpenAPI的團隊打造。
參考《Spring Boot從零入門5_五臟俱全的RESTful Web Service構建》。構建好后有如下REST API:
#?獲取所有用戶信息GET?http://localhost:8080/api/v1/users#?新增一個用戶,參數通過body傳遞POST?http://localhost:8080/api/v1/users#?更新一個用戶信息PUT?http://localhost:8080/api/v1/users/{id}#?刪除指定用戶DELETE?http://localhost:8080/api/v1/users/{id}
構建好RESTful WEB服務后,接下來我們集成Swagger,然后對上節中的REST API自動生成接口文檔。
集成Swagger2,需要在pom.xml中添加依賴源:
<dependencies> <dependency> ????<groupid>io.springfox</groupid> ????<artifactid>springfox-swagger2</artifactid> ????<!--?截至2019年11月7日為止,最新版本為2.9.2?--> ????<!--?https://mvnrepository.com/artifact/io.springfox/springfox-swagger2?--> ????<version>2.9.2</version> </dependency></dependencies>
springfox
有一個專用對象Docket,可以靈活的配置Swagger的各種屬性,首先我們簡單的創建一個Swagger配置類Swagger2Config.java
:
@Configuration@EnableSwagger2 public?class?Swagger2Config?{ ????@Bean("UsersApis") ????public?Docket?usersApis()?{????????return?new?Docket(DocumentationType.SWAGGER_2)????????????????.select()????????????????.apis(RequestHandlerSelectors.any())????????????????.paths(PathSelectors.any())????????????????.build(); ????} }
這里的@Configuration
注解用于定義配置類,被注解的類內部包含有一個或多個被@Bean
注解的方法,這些方法將會被AnnotationConfigApplicationContext
類進行掃描,并用于構建Bean定義,初始化對象。@ComponentScan
會自動獲取所有的Spring Components,包括@Configuration
類。另外這里的“用戶管理模塊”API生成配置很簡單,對所有路徑上API都去生成文檔。
當完成Swagger2的配置類時,啟動WEB服務,通過http://localhost:8080/v2/api-docs就可以訪問生成文檔內容,但是瀏覽器返回的是JSON內容,基本上很難給需要用到相關API的開發人員進行參考。這個時候就需要用到Swagger2 UI
了。
pom.xml添加依賴,然后重啟WEB服務就可以了,再次訪問http://localhost:8080/swagger-ui.html,這時候看到的就是WEB文檔了。
<dependency> ????<groupid>io.springfox</groupid> ????<artifactid>springfox-swagger-ui</artifactid> ????<version>2.9.2</version></dependency>
從swagger-ui頁面看到的內容有一部無關的內容,或者是如何明顯表現跟項目相關的內容呢?下面章節詳細講解Swagger的各種配置,能夠應用到實際生產環境中去。
首先,如果要將我們最后生成的API文檔給生產環境的開發人員查閱,那么友好的展示信息和歸類是很有必要的,我們接下來實現如下目標:
文檔的各種信息說明
文檔標題
文檔描述
文檔版本號
Logo
文檔責任人
文檔許可證信息
文檔服務條款
API分組
組描述
各API描述
附加部分(非API)
定制化文檔頁面風格
為了更好地展示API分組功能,這里另外加了一組REST API (代碼層面上只需要將User相關的代碼全部復制一份,將User關鍵字全部改為Product就可以了,包括大小寫):
#?獲取所有產品信息GET?http://localhost:8080/api/v1/products#?新增一個產品,參數通過body傳遞POST?http://localhost:8080/api/v1/products#?更新一個產品信息PUT?http://localhost:8080/api/v1/products/{id}#?刪除指定產品DELETE?http://localhost:8080/api/v1/products/{id}
@Configuration@EnableSwagger2public?class?Swagger2Config?{????@Bean("UsersApis")????public?Docket?usersApis()?{????????return?new?Docket(DocumentationType.SWAGGER_2)???????? //?select()返回的是ApiSelectorBuilder對象,而非Docket對象 ????????????????.select() ????????????????.apis(RequestHandlerSelectors.any()) ????????????????.paths(PathSelectors.any())????????????????? ????????????????//?build()返回的是Docket對象 ????????????????.build()????????????????//?測試API時的主機URL ????????????????.host("https://xiaobaiai.net")???? ????????????????//?API前綴 ????????????????.pathProvider(new?RelativePathProvider(null)?{????????????????????@Override ????????????????????public?String?getApplicationBasePath()?{????????????????????????return?"/prefix"; ????????????????????} ????????????????}) ????????????????.apiInfo(apiInfo()); ????}???? ????public?ApiInfo?apiInfo()?{???? //?API負責人的聯系信息 ???? final?Contact?contact?=?new?Contact(???? "Ethan",?"https://xiaobaiai.net",?"ycm_hy@163.com");????????return?new?ApiInfoBuilder()???????? //?API文檔標題 ????????????.title("X系統平臺接口文檔")????????????//?API文檔描述 ????????????.description("用戶/產品相關API,?更多請關注公眾號:?小白AI?或微信小程序:小白AI博客")????????????//?服務條款URL ????????????.termsOfServiceUrl("https://github.com/yicm")????????????//?API文檔版本 ????????????.version("1.0")????????????//?API負責人的聯系信息 ????????????.contact(contact)????????????//?API的許可證Url ????????????.licenseUrl("http://license.coscl.org.cn/MulanPSL") ????????????.license("MulanPSL") ????????????.build(); ????} }
通過添加文檔信息編譯對象ApiInfoBuilder
可以配置API文檔的各種信息,包括標題、描述、服務條款、版本、責任人、許可證等。最后在Docket中添加信息配置對象即可生效。
上面的文檔信息配置中默認是沒有對API分組的,即所有的API都展示在了一個頁面,沒有隔離,如果需要分組,那我們需要對不同API組分配Bean,目前示例可以分為用戶API組和產品API組,然后通過apis()
?和?paths()
進行API過濾。
為了不顯示某個包下面API或某個URL路徑下API,?Docket
提供了?apis()
?和?paths()
?兩 個方法來幫助我們在不同級別上過濾接口(上面示例我們默認對這兩個設置是不做任何過濾,掃描所有API):
apis()
:這種方式可以通過指定包名的方式,讓 Swagger2 只去某些包下面掃描
paths()
:這種方式可以通過篩選 API 的 URL 來進行過濾
apis和paths中的Predicates
除了any
、ant
、none
,還支持regex
正則表達式。
如:
PathSelectors.regex("/api/v2/users.*")
下面就是分組示例代碼,實現分組,很簡單,就是在Docket中配置組名就好了:
@Configuration@EnableSwagger2public?class?Swagger2Config?{????@Bean ????public?Docket?usersApis()?{????????return?new?Docket(DocumentationType.SWAGGER_2) ???????? .groupName("用戶管理接口")???????? //?select()返回的是ApiSelectorBuilder對象,而非Docket對象 ????????????????.select() ???????????????? ????????????????.apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.user")) ????????????????.paths(Predicates.or(????????????????????????//?兩個**,可以匹配底下所有URL ????????????????????????//?一個*,只能匹配一級URL分段 ????????????????????????PathSelectors.ant("/api/v1/users/**"), ????????????????????????PathSelectors.ant("/api/v1/users/*")))?????????????? ????????????????//?build()返回的是Docket對象 ????????????????.build()????????????????//?測試API時的主機URL ????????????????.host("https://xiaobaiai.net")???? ????????????????//?API前綴,最終所有API的基礎地址就是host+prefix:?https://xiaobaiai.net/prefix ????????????????.pathProvider(new?RelativePathProvider(null)?{????????????????????@Override ????????????????????public?String?getApplicationBasePath()?{????????????????????????return?"/prefix"; ????????????????????} ????????????????}) ????????????????.apiInfo(apiInfo()); ????}???? ????@Bean ????public?Docket?productsApis()?{????????return?new?Docket(DocumentationType.SWAGGER_2) ???????? .groupName("產品管理接口")???????? //?select()返回的是ApiSelectorBuilder對象,而非Docket對象 ????????????????.select() ???????????????? ????????????????.apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.product")) ????????????????.paths(Predicates.or(????????????????????????//?兩個**,可以匹配底下所有URL ????????????????????????//?一個*,只能匹配一級URL分段 ????????????????????????PathSelectors.ant("/api/v1/products/**"), ????????????????????????PathSelectors.ant("/api/v1/products/*")))?????????????? ????????????????//?build()返回的是Docket對象 ????????????????.build()????????????????//?測試API時的主機URL ????????????????.host("https://xiaobaiai.net")???? ????????????????//?API前綴 ????????????????.pathProvider(new?RelativePathProvider(null)?{????????????????????@Override ????????????????????public?String?getApplicationBasePath()?{????????????????????????return?"/prefix"; ????????????????????} ????????????????}) ????????????????.apiInfo(apiInfo()); ????}???? ????public?ApiInfo?apiInfo()?{???? //?API負責人的聯系信息 ???? final?Contact?contact?=?new?Contact(???? "Ethan",?"https://xiaobaiai.net",?"ycm_hy@163.com");????????return?new?ApiInfoBuilder()???????? //?API文檔標題 ????????????.title("X系統平臺接口文檔")????????????//?API文檔描述 ????????????.description("用戶/產品相關API,?更多請關注公眾號:?小白AI?或微信小程序:小白AI博客")????????????//?服務條款URL ????????????.termsOfServiceUrl("https://github.com/yicm")????????????//?API文檔版本 ????????????.version("1.0")????????????//?API負責人的聯系信息 ????????????.contact(contact)????????????//?API的許可證Url ????????????.licenseUrl("http://license.coscl.org.cn/MulanPSL") ????????????.license("MulanPSL") ????????????.build(); ????} }
分組配置完成后,重新啟動,打開瀏覽器就可以看到效果了:
雖然上面我們已經可以控制API的顯示和分組了,但是對于API一些更詳細,對組內API再次歸類之類的,比如小組的描述信息,以及每個API如何去控制它的參數說明,返回值說明等。這些都是通過注解去實現的,接下來我們講述常用的注解及作用:
@Api
?: 將這個注解添加到控制器類上,則可以給控制器添加描述類信息:
相關可設置參數有:
value: 用作承載資源的API聲明的“路徑”,可以說是API URL的別名
tags:如果設置這個值、value的值會被覆蓋
description:已過時,對api資源的描述
protocols:協議類型如: http, https, ws, wss.
hidden:配置為true ,隱藏此資源下的操作(試驗了下,貌似無法生效,替代方案還是用@ApiIgnore吧)
produces:如 “application/json, application/xml”
consumes: 如 “application/json, application/xml”
authorizations:高級特性認證時配置
示例:
//?Swagger配置類@Configuration@EnableSwagger2public?class?Swagger2Config?{@Bean ????public?Docket?productsApis()?{????????return?new?Docket(DocumentationType.SWAGGER_2) ???????? .groupName("產品管理接口") ????????????????.select() ????????????????.apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.product")) ????????????????.paths(Predicates.or( ????????????????????????PathSelectors.ant("/api/v1/products/**"), ????????????????????????PathSelectors.ant("/api/v1/products/*")))?????????????? ????????????????.build() ????????????????.host("https://xiaobaiai.net")???? ????????????????.pathProvider(new?RelativePathProvider(null)?{????????????????????@Override ????????????????????public?String?getApplicationBasePath()?{????????????????????????return?"/prefix"; ????????????????????} ????????????????}) ????????????????.apiInfo(apiInfo()) ????????????????.tags(new?Tag("產品操作分組1",?"產品查詢相關操作."),????????????????????????new?Tag("產品操作分組2",?"產品添加或刪除相關操作."),????????????????????????new?Tag("產品操作分組3",?"產品更新相關操作."),????????????????????????new?Tag("產品操作分組4",?"產品相關全部操作.")); ????} }
//?控制器類@RestController@RequestMapping("/api/v1")@Api(tags={"產品接口文檔列表"})public?class?ProductServiceController?{?...?}
效果如下:
@ApiIgnore
: 作用在REST API控制器方法
上,則該API不會被顯示出來:
@ApiIgnore@RequestMapping(value?=?"/users/{id}",?method?=?RequestMethod.DELETE)public?ResponseEntity<object>?delete(@PathVariable("id")?String?id)?{?...?}
@ApiOperation
?注解用于控制器方法
上面,用于對方法的描述,相關參數設置描述如下:
value:接口的名稱
notes:接口注意點說明
response: 接口的返回類型,比如說:response = String.class
hidden: 配置為true 將在文檔中隱藏
示例:
@ApiOperation(value?=?"獲取所有產品",?notes?=?"每調用一次,就耗費流量100M",?response?=?String.class)@GetMapping(value?=?"/products")public?ResponseEntity<object>?getProduct()?{ return?new?ResponseEntity<>(productService.getProducts(),?HttpStatus.OK); }
最后效果就是:
@ApiImplicitParams
和@ApiImplicitParam
?注解用于控制器方法傳入參數的說明。默認情況下,Swagger會根據API方法中的傳入參數進行參數說明的生成,不過參數說明默認就是變量名,因為這兩個注解不一定需要。相關參數設置說明如下:
name:參數名稱,注意一定要與實際方法的形參名一致,否則無法生效
value:參數值
defaultValue:參數默認值
required:是否為必需項
allowMultiple: 是否允許重復
dataType: 數據類型,如object,string,array,int,等
paramType:參數傳遞類型
header : 放在請求頭。請求參數的獲?。?code >@RequestHeader(代碼中接收注解)
query : 用于get請求的參數拼接。請求參數的獲?。?code >@RequestParam(代碼中接收注解)
path : 用于restful接口,請求參數的獲?。?code >@PathVariable(代碼中接收注解)
body : 放在請求體。請求參數的獲?。?code >@RequestBody(代碼中接收注解)
form : 不常用
examples: 示例
示例:
//?如果只有一個參數,則僅僅@ApiImplicitParam就可以了@ApiImplicitParams({ @ApiImplicitParam(name="id",?value="產品ID值",?required?=?true), @ApiImplicitParam(name="product",?value="產品內容",?required?=?true) })@RequestMapping(value?=?"/products/{id}",?method?=?RequestMethod.PUT)public?ResponseEntity<object>?updateProduct(@PathVariable("id")?String?id,?@RequestBody?Product?product)?{ productService.updateProduct(id,?product); return?new?ResponseEntity<>("Product?is?updated?successsfully",?HttpStatus.OK); }
焦作國醫堂胃腸醫院評價怎么樣:http://jz.lieju.com/zhuankeyiyuan/37174965.htm
@ApiParam
: 作用同ApiImplicitParam,單個參數描述一般常用該注解,而且該注解只能與JAX-RS 1.x/2.x注解結合使用。參數設置說明如下:
name: 參數名稱
value: 參數值
required: 是否為必須項
defaultValue: 默認值
type: 參數類型
hidden: 是否因此該參數
@ApiResponses
、@ApiResponse
: 用于控制器方法返回值的說明,參數設置說明如下:
code: http的狀態碼
message:返回狀態描述
response: 狀態響應,默認響應類為Void
示例:
@ApiOperation(value?=?"獲取所有產品",?notes?=?"每調用一次,就耗費流量100M",response?=Product.class,?responseContainer="List")@ApiResponses({????@ApiResponse(code?=?200,?message?=?"成功!",?response=Product.class),????@ApiResponse(code?=?401,?message?=?"未授權!",?response=Product.class),????@ApiResponse(code?=?404,?message?=?"頁面未找到!",?response=Product.class),????@ApiResponse(code?=?403,?message?=?"出錯了!",?response=Product.class) })@GetMapping(value?=?"/products")public?ResponseEntity<object>?getProduct()?{ return?new?ResponseEntity<>(productService.getProducts(),?HttpStatus.OK); }
效果如下:
@Deprecated
: 作用于控制器方法上,標注該方法已經過時,建議開發者采用新的方式之類的。
@ApiModel
:作用在JavaBean類上,說明JavaBean的用途,如我們定義的Product.java類。常用參數設置如下:
value: 實體類別名,默認為類名字
description: 描述
parent: 父類,默認為Void.class
subTypes: 默認為{}
reference: 依賴,默認為""
示例:
@ApiModel(value="Product",description="對產品定義的描述")public?class?Product?{?...?}
@ApiModelProperty
: 同樣用于在JavaBean類的屬性上面,說明相關屬性。類似于方法上說明的@ApiImplicitParam
。設置參數有:
name: 屬性名稱,需與JavaBean內保持一致
value: 屬性值
notes: 說明
dataType: 數據類型
required: 是否必須
readOnly: 是否只讀,默認為false
reference: 依賴,默認為""
allowEmptyValue: 是否允許空值
allowableValues: 允許值,默認為""
管理不同API版本有好幾種方式:
通過URL的方式,將版本號包含在URL中,如/api/v1/users
。通過這種方式,我們可以在Docket中過濾出不同版本,結合分組,可以實現不同版本的API管理。
通過查詢參數,將版本號作為一個具體參數,如/api/users?version=1
通過自定義HTTP頭–定義一個新的頭,其中包含請求中的版本號
通過內容(Content)協商:版本號與接受的內容類型一起包含在“Accept”頭中,如curl -H "Accept: application/vnd.piomin.v1+json" http://localhost:8080/api/users
這里我們對第一種方式示例展示:
在Swagger2Config.java中新添加一個用戶API Docket:
@Configuration@EnableSwagger2public?class?Swagger2Config?{????@Bean("UsersApis_V1")????public?Docket?usersApisV1()?{????????return?new?Docket(DocumentationType.SWAGGER_2) ???????? .groupName("用戶管理接口V1") ????????????????.select() ????????????????.apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.user")) ????????????????.paths(Predicates.or(????????????????????????//?過濾版本v1 ????????????????????????PathSelectors.ant("/api/v1/users/**"), ????????????????????????PathSelectors.ant("/api/v1/users/*")))?????? ????????????????.build() ????????????????.host("https://xiaobaiai.net")???? ????????????????.pathProvider(new?RelativePathProvider(null)?{????????????????????@Override ????????????????????public?String?getApplicationBasePath()?{????????????????????????return?"/prefix"; ????????????????????} ????????????????}) ????????????????.apiInfo(apiInfo()); ????}???? ????@Bean("UsersApis_V21")????public?Docket?usersApisV2()?{????????return?new?Docket(DocumentationType.SWAGGER_2) ???????? .groupName("用戶管理接口V2") ????????????????.select() ????????????????.apis(RequestHandlerSelectors.basePackage("com.xiaobaiai.user")) ????????????????.paths(Predicates.or(????????????????????????//?過濾版本v1 ????????????????????????PathSelectors.ant("/api/v2/users/**"), ????????????????????????PathSelectors.ant("/api/v2/users/*")))?????? ????????????????.build() ????????????????.host("https://xiaobaiai.net")???? ????????????????.pathProvider(new?RelativePathProvider(null)?{????????????????????@Override ????????????????????public?String?getApplicationBasePath()?{????????????????????????return?"/prefix"; ????????????????????} ????????????????}) ????????????????.apiInfo(apiInfo()); ????}???? ????@Bean ????public?Docket?productsApis()?{????????return?new?Docket(DocumentationType.SWAGGER_2) ???????? ..... ????}???? ????public?ApiInfo?apiInfo()?{???? final?Contact?contact?=?new?Contact(???? "Ethan",?"https://xiaobaiai.net",?"ycm_hy@163.com");????????return?new?ApiInfoBuilder() ????????????.title("X系統平臺接口文檔") ????????????.description("用戶/產品相關API,?更多請關注公眾號:?小白AI?或微信小程序:小白AI博客") ????????????.termsOfServiceUrl("https://github.com/yicm") ????????????.version("1.0") ????????????.contact(contact) ????????????.licenseUrl("http://license.coscl.org.cn/MulanPSL") ????????????.license("MulanPSL") ????????????.build(); ????} }
然后控制器添加新版本API方法:
@RestController@RequestMapping("/api")public?class?UserServiceController?{ @Autowired UserService?userService; @DeleteMapping({"/v1/users/{id}",?"/v2/users/{id}"}) public?ResponseEntity<object>?delete(@PathVariable("id")?String?id)?{ userService.deleteUser(id); return?new?ResponseEntity<>("User?is?deleted?successsfully",?HttpStatus.OK); } @PutMapping({"/v1/users/{id}",?"/v2/users/{id}"}) public?ResponseEntity<object>?updateUser(@PathVariable("id")?String?id,?@RequestBody?User?user)?{ userService.updateUser(id,?user); return?new?ResponseEntity<>("User?is?updated?successsfully",?HttpStatus.OK); } @PostMapping({"/v1/users",?"/v2/users"}) public?ResponseEntity<object>?createUser(@RequestBody?User?user)?{ userService.createUser(user); return?new?ResponseEntity<>("User?is?created?successfully",?HttpStatus.CREATED); } @GetMapping({"/v1/users"}) @Deprecated public?ResponseEntity<object>?getUser()?{ return?new?ResponseEntity<>(userService.getUsers(),?HttpStatus.OK); } @GetMapping(value?=?"/v2/users") public?ResponseEntity<object>?getUser(@RequestParam?String?id)?{ return?new?ResponseEntity<>(userService.getUsers(),?HttpStatus.OK); } }
最后效果:
向AI問一下細節
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。