一句話歸納:對於產品開發,特別是先後端分離的開發模式,接口文檔是鏈接先後端的樞紐,本文對springboot+swagger在企業的實踐進行詳細描述。
在軟件開發過程當中,接口設計與接口文檔編寫是重要的一環,特別是在先後端分離的狀況下,接口說明文檔是開發人員之間的鏈接點。接口文檔的編寫有不少方式,可使用word,也可使用編寫工具如小幺雞,這些基本屬於脫離代碼編寫的文檔,若是接口變化,須要額外修改文檔,增長工做量。如何提升寫接口文檔效率,在springboot開發中,結合swagger來提供接口文檔說明是一個很好的實踐,經過配置與註解,在編寫接口代碼過程當中,同時也把接口文檔寫好,接口須要變動時,文檔也同時變動,具備工做量少,效率高,接口文檔直觀簡潔,可實時調用驗證等好處。本文基本springboot2+swagger2,結合在企業中的實踐,對接口文檔的編寫進行詳細說明,具體有以下內容:html
如需看源碼,本文示例工程地址:https://github.com/mianshenglee/my-example
前端
swagger官網地址: https://swagger.io
,從官網看出,它是一個規範 (OpenAPI Specification,OAS) 和完整的框架(如編輯器 Swagger Editor ,顯示組件 Swagger Editor ,代碼生成 Swagger Codegen ),用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。既然是規範,它定義了一系列與接口描述相關的規則,如文檔格式,文件結構,數據類型,請求方法等等,可參考官方規範說明: https://swagger.io/specification/v2/
,文件設計和編寫時通常是 YAML格式 (方便編寫),在傳輸時則會使用JSON(通用性強)。有了接口描述文件後,須要進行友好顯示,swagger提供了Swagger-UI: https://swagger.io/tools/swagger-ui/
。這個組件的做用就是把已經按規範寫好的yaml文件,經過接口文檔形式顯示。咱們能夠下載這個組件本地運行,它是一個靜態網頁,放到web容器(如apache,nginx)來運行。也可使用官網的在線版本。以下:java
至此,咱們知道使用swagger進行接口文檔編寫步驟是:(1)編寫符合OAS的接口文件,(2)使用swagger-ui進行顯示。nginx
從前面可知,編寫符合OAS的接口文件仍是得手工編寫,swagger-ui顯示也不方便(須要人工離線部署或使用在線服務),如今java領域的web開發,基本都使用springmvc開發,有沒有更方便的方式來結合swagger進行接口文件編寫,而不須要 手工編寫,減小工做量?git
牛人 Marty Pitt
編寫了一個基於Spring的組件swagger-springmvc:https://github.com/martypitt/swagger-springmvc-example
,用於將swagger集成到springmvc中來。springfox則是從這個組件發展而來,它能夠基於spring自動生成JSON的API文檔,如今經過https://github.com/martypitt/swagger-springmvc/
地址會直接跳轉到springfox的github頁面,它的官網:http://springfox.io
。當前springfox已是發佈了多個版本,本文使用的是springfox-swagger2
的2.7.0的版本。對於swagger-ui,它也提供了springfox-swagger-ui
,集成接口文檔顯示頁面。這樣,前面swagger的兩個步驟均可以自動完成。而如今咱們開發java web應用基本都是使用springboot進行開發,使用它的自動配置與註解,更加方便開發。所以,基於swagger的規範,使用springfox的組件(swagger2和swagger-ui),結合springboot,可讓接口文檔變得很是簡單,有效。總的來講,springfox是對swagger規範的在springmvc和springboot開發中的自動化實現。本文後續就是以這種方式進行具體的使用描述。github
本章節將使用springboot2+swagger2構建示例工程,並對基本配置進行描述。web
(1) 建立項目:經過 Spring Initializr 頁面生成一個空 Spring Boot 項目,添加web依賴。spring
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
(2)編寫示例接口:apache
@RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; /** * 查詢單個用戶 */ @GetMapping("/{userId}") public ResponseResult getUserById(@PathVariable long userId) {...} /** * 查詢多個用戶 */ @GetMapping() public ResponseResult getUsers() {...} /** * 添加多個用戶 */ @PostMapping() public ResponseResult addUsers(@RequestBody List<User> users) {...} /** * 添加單個用戶 */ @PostMapping("/user") public ResponseResult addUser(@RequestBody User user) {...} /** * 更新單個用戶 */ @PutMapping("/user") public ResponseResult updateUser(@RequestBody User user) {...} /** * 刪除單個用戶 */ @DeleteMapping("/user") public ResponseResult deleteUser(long userId) {...} }
注意:此處咱們只關注接口的定義、參數和返回值,具體實體不列出(詳細可看
源碼:
https://github.com/mianshenglee/my-example
)。
至此,示例工程能夠正常運行,具備如下接口:json
GET /users/{userId}
獲取單個用戶GET /users
獲取多個用戶POST /users
添加多個用戶POST /users/user
添加單個用戶PUT /users/user
更新單個用戶DELTE /users/user?userId=xx
刪除單個用戶注意:接口的RequestMapping,若是不指定httpMethod,springfox會把這個方法的全部動做GET/POST/PUT/PATCH/DELETE的方法都列出來,所以,寫接口時,請指定實際的動做。
爲了能夠自動化生成符合swagger的接口描述文件,須要添加springfox的依賴,以下:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> </dependency>
添加config包用於存放配置文件,添加如下Swagger2Config.java配置,以下:
@Configuration @EnableSwagger2 public class Swagger2Config { private ApiInfo apiInfo() { @Bean public Docket api(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder().title("接口說明文檔") .description("API文檔描述") .version("0.0.1-SNAPSHOT") .build(); } }
此文件配置了Docket的Bean,包括api的基本信息,須要顯示的接口(apis),須要過濾的路徑(paths),這樣,就能夠生成符合規範的接口文檔。
使用上面的配置集成swagger2後,啓動項目,輸入 http://localhost:8080/swaggerdemo/v2/api-docs
便可查看JSON格式的接口描述文檔。以下所示:
此文檔是符合OAS規範,但離可視化顯示和使用還差一步,就是如何把這些信息展現在界面上。
爲了能可視化顯示接口文檔,springfox已提供界面顯示組件,添加如下依賴:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.version}</version> </dependency>
添加此依賴後,引入的springfox-swagger-ui.jar包中有swagger-ui.html
頁面,使用地址 http://localhost:8080/swaggerdemo/swagger-ui.html
便可查看接口文檔頁面。在此文檔中,能夠查看接口定義、輸入參數、返回值,也可使用try it out
調用接口進行調試。以下:
至此,經過前面示例,已經以最簡單的方式實現了使用swagger對接口文檔的自動生成和UI顯示,但相對於企業中實際開發使用接口文檔,仍是有必定距離,包括:
針對這些問題,在企業實踐中,須要進行處理。
一份接口文檔,通常都會有關於此文檔的基本信息,包括文檔標題、描述、接口版本、聯繫人等信息,swagger已提供了對應的配置項,如上面的示例中的apiInfo()
,把基本信息寫在代碼中進行配置,這樣對修改不友好,所以,最好是把這些配置參數化,造成swagger的獨立配置文件,避免修改基本信息致使代碼的改動。
在resources/config
目錄下,添加swagger.properties
文件,把配置信息放到此文件中。
swagger.basePackage =me.mason.helloswagger.demo.controller swagger.title = 應用API說明文檔 swagger.description = API文檔描述 swagger.version = 0.0.1-SNAPSHOT swagger.enable = true swagger.contactName = mason swagger.contactEmail = swagger.contactUrl = swagger.license = swagger.licenseUrl =
對應的,在config
包下,添加SwaggerInfo對象,對應上述配置文件:
@Component @ConfigurationProperties(prefix = "swagger") @PropertySource("classpath:/config/swagger.properties") @Data public class SwaggerInfo { private String basePackage; private String antPath; private String title = "HTTP API"; private String description = "Swagger 自動生成接口文檔"; private String version ; private Boolean enable; private String contactName; private String contactEmail; private String contactUrl; private String license; private String licenseUrl; }
官方建議對於這種自定義的配置,最好添加如下spring-boot-configuration-processor
依賴,以生成配置元數據,不然在IDEA中,會有"spring boot configuration annotation processor not found in classpath"
的警告:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
有了SwaggerInfo
配置類,在集成swagger2的apiInfo()
能夠注入使用,之後須要改動時修改配置文件便可。
private ApiInfo apiInfo() { return new ApiInfoBuilder().title(swaggerInfo.getTitle()) .description(swaggerInfo.getDescription()) .version(swaggerInfo.getVersion()) .licenseUrl(swaggerInfo.getLicenseUrl()) .contact( new Contact(swaggerInfo.getContactName() ,swaggerInfo.getContactUrl() ,swaggerInfo.getContactEmail())) .build(); }
對於不一樣環境,有可能須要啓用和關閉接口文檔,如在開發環境咱們須要顯示接口文檔,但在正式線上環境,有時咱們不但願對外公佈接口。此需求可使用enable
配置便可,結合配置文件中有swagger.enable
配置項(false關閉,true啓用)。以下:
.enable(swaggerInfo.getEnable())
swagger組件會自動掃描有Controller
註解和RequestMapping
註解的接口,轉換爲接口描述。在開發過程當中,咱們不但願項目中全部的接口都顯示出來,而是想把特定的包下的接口顯示便可。使用方法apis()
,結合配置文件,可達到此效果。配置文件中swagger.basePackage
配置(多個包使用";"分隔)。添加如下過濾函數:
private List<Predicate<RequestHandler>> apisFilter() { List<Predicate<RequestHandler>> apis = new ArrayList<>(); String basePackageStr = swaggerInfo.getBasePackage(); //包過濾 if (StrUtil.isNotEmpty(basePackageStr)) { //支持多個包 String[] basePackages = basePackageStr.split(";"); if (null != basePackages && basePackages.length > 0) { Predicate<RequestHandler> predicate = input -> { // 按basePackage過濾 Class<?> declaringClass = input.declaringClass(); String packageName = declaringClass.getPackage().getName(); return Arrays.asList(basePackages).contains(packageName); }; apis.add(predicate); } } return apis; }
上述功能是經過掃描到的接口與配置的包比較,在配置的包下則返回,不然過濾掉。而後在配置Docket時,添加此過濾。
builder = builder.apis(Predicates.or(apisFilter()));
此處使用Predicates.or
,表示對返回的進行or操做,true的即顯示。
通過上面的配置化,咱們已經能夠靈活構建文檔了。但文檔的內容還不豐富,對接口的描述不詳細,springfox
把對接口的描述都經過註解的方式來完成,主要包括如下幾種註解:
@ApiModel
,實體描述;@ApiModelProperty
,實體屬性描述@ApiOperation
,接口描述;@ApiIgnore
,忽略此接口@Api
,控制器描述@ApiImplicitParams
,接口多個參數;@ApiImplicitParam
,單個參數;@ApiParam
,單個參數描述@ResponseHeader
,響應值header;@ApiResponses
,響應值集;@ApiResponse
,單個響應值下面咱們對註解的使用進行說明,並用註解添加示例中的接口描述。
開發過程當中,對於MVC模式的接口,使用M(Model)進行數據通訊,所以,須要對此數據模型進行有效的描述。對應的註解有:
@ApiModel
:實體描述@ApiModelProperty
:實體屬性描述。下表爲@ApiModelProperty
的使用說明:
註解屬性 | 類型 | 描述 |
---|---|---|
value | String | 字段說明。 |
name | String | 重寫字段名稱。 |
dataType | Stirng | 重寫字段類型。 |
required | boolean | 是否必填。 |
example | Stirng | 舉例說明。 |
hidden | boolean | 是否在文檔中隱藏該字段。 |
allowEmptyValue | boolean | 是否容許爲空。 |
allowableValues | String | 該字段容許的值。 |
本示例中,即User
類,使用以下註解進行描述:
@ApiModel public class User { @ApiModelProperty(value="id",required = true,example = "1") private long id; @ApiModelProperty(value="姓名",required = true,example = "張三") private String name; @ApiModelProperty(value="年齡",example = "10") private int age; @ApiModelProperty(value="密碼",required = true,hidden = true) private String password; }
這樣,在接口文檔中,使用User
做爲輸入參數時,能夠看到這個模型的描述,以下:
springfox 在自動化生成文檔時,若是不使用註解進行描述,控制器和接口基本是把代碼的類名,方法名,接口映射做爲顯示的內容。對於控制器和具體某個接口的描述,分別有:
@ApiIgnore
: Swagger 文檔不會顯示擁有該註解的接口@Api
: 對控制器的描述註解屬性 | 類型 | 描述 |
---|---|---|
tags | String[] | 控制器標籤。 |
description | String | 控制器描述(該字段被申明爲過時)。 |
@ApiOperation
: 對接口的描述。註解屬性 | 類型 | 描述 |
---|---|---|
value | String | 接口說明。 |
notes | String | 接口發佈說明。 |
tags | Stirng[] | 標籤。 |
response | Class<?> | 接口返回類型。 |
httpMethod | String | 接口請求方式。 |
在本示例中,在UserController
中進行相應描述,以下:
@Api(tags = "用戶管理接口", description = "User Controller") public class UserController { @ApiOperation(value = "根據ID獲取單個用戶信息", notes = "根據ID返回用戶對象") ...//省略 @ApiOperation(value = "添加多個用戶", notes = "使用JSON以數組形式添加多個用戶") ...//其它 }
使用這些註解後,顯示的文檔以下:
在介紹Swagger的接口輸入參數註解說明前,有必要對SpringMVC的接口參數有一個清晰的瞭解。咱們知道SpringMVC是經過@RequestMapping
把方法與請求URL對應起來,而調用接口方法時須要把參數傳給控制器。通常來講,SpringMVC的參數傳遞包括如下幾種:
@RequestParam
註解參數爲方便解釋,在本示例中,提供了ParamDemoController
文件,裏面對應給出了各類參數的示例,讀者可參考查看,下面對這幾種參數進行描述。
(1)無註解參數
無註解下獲取參數,要求參數名稱與HTTP請求參數名稱一致,參數能夠爲空。以下接口:
@GetMapping("/no/annotation") public Map<String,Object> noAnnotation(Integer intVal, Long longVal, String str){}
調用時,可以使用http://localhost:8080/no/annotation?inVal=10&longVal=100
,參數str
可爲空。
(2)@RequestParam
註解參數
使用註解RequestParam獲取參數,能夠指定HTTP參數和方法參數名的映射,並且不能爲空(但可經過required設置false來容許爲空)。以下接口:
@GetMapping("/annotation") public Map<String,Object> annotation(@RequestParam("int_val") Integer intVal,@RequestParam("long_val") Long longVal,@RequestParam(value="str_val", required = false) String str){}
調用方法跟上面無註解的同樣。但若是沒有設置required=false
,則必須傳入str
參數。
(3)數組參數
使用數組作爲參數,調用接口時,數組元素使用逗號(,
)分隔。以下接口:
@GetMapping("/array") public Map<String,Object> requestArray(int[]intArr,long[]longArr,String[] strArr){}
調用此接口,可以使用http://localhost:8080/array?intArr=10,11,12&longArr=100,101,102&strArr=a,b,c
。
(4)JSON對象參數
當傳輸的數據相對複雜,或者與定義的模型相關,則須要把參數組裝成JSON進行傳遞,當前端調用接口時使用JSON
請求體,SpringMVC可經過@RequestBody
註解,實現JSON參數的映射,並進行合適的對象轉換。以下:
@PostMapping("/add/user") public User addUser(@RequestBody User user){}
調用接口時,使用JSON傳遞參數,經過RequestBody註解獲得JSON參數,映射轉換爲User對象。若是是數組(List)對象,一樣支持。
(5)REST風格參數
對於REST風格的URL,參數是寫在URL中,如users/1
,其中1
是用戶ID參數。對於此類參數,SpringMVC使用@PathVariable
進行實現,其中使用{}
做爲佔位符。以下:
@GetMapping("/users/{id}") public User getUser(@PathVariable("id") Long id){}
對於上面提到的的SpringMVC參數,springfox已經作了自動處理,在swagger-ui
文檔中顯示時,也會根據參數類型進行顯示。在swagger中,參數類型分爲:
@RequestParam
註解參數在描述輸入參數時,swagger在如下註解:
@ApiImplicitParams
: 描述接口的參數集。@ApiImplicitParam
: 描述接口單個參數,與 @ApiImplicitParams
組合使用。@ApiParam
:描述單個參數,可選其中,@ApiParam
與單個參數一塊兒用,@ApiImplicitParam
使用在接口描述中,二者的屬性基本一致。@ApiImplicitParam
的屬性以下所示:
註解屬性 | 描述 |
---|---|
paramType | 查詢參數類型,表示參數在哪裏,前面已提到,可取值: path,query,header,form,body。 |
dataType | 參數的數據類型, 只做爲標誌說明,並無實際驗證,可按實際類型填寫,如String,User |
name | 參數名字 |
value | 參數意義的描述 |
required | 是否必填:true/false |
allowMultiple | 是否有多個,在使用JSON數組對象時使用 |
example | 數據示例 |
如下是對使用JSON對象數組做爲參數的接口,以下:
@ApiOperation(value = "添加多個用戶", notes = "使用JSON以數組形式添加多個用戶") @ApiImplicitParams({ @ApiImplicitParam(name = "users", value = "用戶JSON數組", required = true, dataType = "User",allowMultiple = true) }) @PostMapping() public ResponseResult addUsers(@RequestBody List<User> users) {}
此示例中,使用@RequestBody
註解,傳遞List<User>
的JSON數組參數,所以,使用了allowMultiple
屬性,dataType爲User
,而它的paramType
爲body
。swagger文檔以下:
對於使用了@RestController
註解的控制器,SpringMVC會自動把返回的數據轉爲json數據。咱們在開發過程當中,通常都會把返回數據作統一封裝,返回狀態,信息,實際數據等等。如本示例中,以ResponseResult
做爲統一返回結果,返回的數據使用泛型(若不使用泛型,後續swagger在顯示返回結果時對於Model的屬性則顯示不出來),以下:
@NoArgsConstructor @AllArgsConstructor @Data public class ResponseResult<T> { @ApiModelProperty(value="返回狀態",example = "true") private boolean success; @ApiModelProperty(value="錯誤信息代碼") private String errCode; @ApiModelProperty(value="錯誤顯示信息") private String errShowMsg; @ApiModelProperty(value="錯誤信息") private String errMsg; @ApiModelProperty(value = "返回數據",notes = "若返回錯誤信息,此數據爲null或錯誤信息對象") private T resultData; }
在接口返回值中,若不使用泛型指定具體Model,只返回ResponseResult
,則只會顯示此對象中的屬性,而具體的resultData中的內容沒法顯示,以下:
所以,對於統一返回,須要指定具體返回的模型,這樣,對於實際返回的模型就有對應的描述。以下:
@ApiOperation(value = "獲取全部用戶", notes = "返回所有用戶") @GetMapping() public ResponseResult<List<User>> getUsers() {}
對於響應消息,swagger提供了默認的401,403,404的響應消息,也能夠本身針對接口進行指定,有如下註解可以使用:
@ApiResponses
:響應消息集 @ApiResponses
:響應消息, 描述一個錯誤的響應信息 ,與@ApiResponses
一塊兒使用@ResponseHeader
:響應頭設置對於對外發布的接口,通常都須要進行權限校驗,須要登陸的用戶才能夠調用,不然報權限不足。對於api的安全認證,通常有如下幾種模式(可參考swagger文檔: https://swagger.io/docs/specification/authentication/
):
Authorization
請求頭,用戶名和密碼使用Base64編碼,如:Authorization: Basic ZGVtbzpwQDU1dzByZA==
Authorization
請求頭,由服務端產生加密字符token,客戶端發送請求時把此token的請求頭髮到服務端做爲憑證,token格式是Authorization: Bearer <token>
對於先後端分離的的springboot項目,如今不少採用jwt的認證方式(屬於Bearer認證),須要先獲取token,而後在調用接口時,添加相似Authorization: Bearer <token>
的請求頭來對接口進行認證。針對這種方式,在Swagger中有如下三種方法來實現:
@ApiImplicitParam
,其中參數類型是ParamType=header
針對須要認證的接口,直接使用@ApiImplicitParam
,其中參數類型是ParamType=header
便可,參數的描述前面已有介紹。以下:
@ApiImplicitParam(name = "Authorization", value = "token,格式: Bearer <token>", required = false, dataType = "String",paramType = "header")
這種方式的缺點是,針對每個接口,都須要添加這個參數描述,而描述都是同樣的,重複工做。
swagger配置中,方法globalOperationParameters
能夠設置全局的參數。
//全局header參數 ParameterBuilder tokenPar = new ParameterBuilder(); List<Parameter> pars = new ArrayList<Parameter>(); tokenPar.name("Authorization").description("token令牌") .modelRef(new ModelRef("string")) .parameterType("header") .required(true).build(); pars.add(tokenPar.build()); docket.globalOperationParameters(pars);
這種方式缺點也明顯,因爲設置了全局參數,則全部接口都須要此參數,如有某些接口不須要,則須要進行特殊處理。
設置認證模式,並使用正則式對須要認證的接口進行篩選,這樣swagger界面提供統一的認證界面。以下:
docket.securitySchemes(securitySchemes()) .securityContexts(securityContexts()); ...//省略 private List<ApiKey> securitySchemes() { return Lists.newArrayList( new ApiKey("Authorization", "Authorization", "header")); } private List<SecurityContext> securityContexts() { return Lists.newArrayList( SecurityContext.builder() .securityReferences(defaultAuth()) //正則式過濾,此處是全部非login開頭的接口都須要認證 .forPaths(PathSelectors.regex("^(?!login).*$")) .build() ); } List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "認證權限"); return Lists.newArrayList( new SecurityReference("Authorization", new AuthorizationScope[]{authorizationScope})); }
設置後,輸入登陸便可,效果以下:
本篇文章針對swagger的使用和企業實踐進行了詳細描述,主要介紹了swagger的原理,如何使用springboot與swagger結合建立接口文檔,對swagger進行參數化配置及包過濾,swagger註解的詳細使用,接口認證方法等,本文中使用的示例代碼已放在github:https://github.com/mianshenglee/my-example
,有興趣的同窗能夠pull代碼,結合示例一塊兒學習。
https://swagger.io/
http://springfox.github.io/springfox/
https://www.ibm.com/developerworks/cn/java/j-using-swagger-in-a-spring-boot-project/index.html
https://www.jianshu.com/p/7a24d202b395
https://mp.weixin.qq.com/s/1KuBFfMugJ4pf3cSEFfXfw
關注個人公衆號,獲取更多技術記錄: