根據yaml文件生成對應的客戶端、服務端代碼
前言
對於早期的webservice接口,我們可以根據wsdl文件生成對應的客戶端和服務端代碼。那麼同樣的針對於Restful風格的接口,也有同樣的根據可以生成對應的客戶端和服務端代碼。這裏我主要講解一種根據yaml描述文檔生成客戶端、服務端代碼的方式是Swagger。
Swagger介紹
相信很多人對於Swagger並不陌生,甚至也使用過Swagger,但是提到Swagger,大部分應該用到的更多是藉助Swagger來生成對應的API文檔,要不然也不至於我苦苦找不到相關資料。當然生成API文檔的方法有很多,大家可以自行百度,在這裏不是我要講述的重點。今天在這裏我重點要講述的是----如何通過Swagger來生成客戶端和服務端代碼。
Swagger是一組開源項目,官網地址是(https://swagger.io/),並提供了一系列的組件,可以參見github地址(https://github.com/swagger-api/)
- **swagger-core :**用於生成Swagger API規範的示例和服務器集成,可輕鬆訪問REST API
- **swagger-codegen :**通過Codegen 可以將描述文件生成對應的客戶端、服務端代碼及API文檔
- **swagger-ui :**一個無依賴的HTML、JS和CSS集合,可以爲Swagger兼容API動態生成優雅文檔
- **swagger-editor :**swagger官方提供的編輯器,用於編寫API設計文檔
接下來我會着重介紹下swagger-codegen的使用
Swagger-codegen
swagger-codegen在github上官方沒有直接發佈的版本,所以我們需要將其源碼從github上下載下來自行編譯,編譯過程也很簡單,使用說明在首頁的readme中詳細描述,只不過是英文的,那我在這裏就做個簡單的轉移吧。github地址:https://github.com/swagger-api/swagger-codegen
編譯swagger-codegen
- 執行命令,從github上將源碼down下來
git clone https://github.com/swagger-api/swagger-codegen.git
- 切換到swagger-codegen目錄下,執行maven打包命令,生成swagger-codengen-cli.jar
mvn clean package
- 生成的swagger-codengen-cli.jar在./modules/swagger-codegen-cli/target下,如圖所示:
swagger-codengen-cli.jar常用指令
- 查看幫助文檔
java -jar swagger-codegen-cli.jar help
- 查看支持的語言
java -jar swagger-codegen-cli.jar langs
輸出的結果如下,這是當前2.4.13版本支持的語言如下:
Available languages: [ada, ada-server, akka-scala, android, apache2, apex, aspnetcore, bash, csharp, clojure, cwiki, cpprest, csharp-dotnet2, dart, dart-jaguar, elixir, elm, eiffel, erlang-client, erlang-server, finch, flash, python-flask, go, go-server, groovy, haskell-http-client, haskell, jmeter, jaxrs-cxf-client, jaxrs-cxf, java, inflector, jaxrs-cxf-cdi, jaxrs-spec, jaxrs, msf4j, java-pkmst, java-play-framework, jaxrs-resteasy-eap, jaxrs-resteasy, javascript, javascript-closure-angular, java-vertx, kotlin, lua, lumen, nancyfx, nodejs-server, objc, perl, php, powershell, pistache-server, python, qt5cpp, r, rails5, restbed, ruby, rust, rust-server, scala, scala-gatling, scala-lagom-server, scalatra, scalaz, php-silex, sinatra, slim, spring, dynamic-html, html2, html, swagger, swagger-yaml, swift5, swift4, swift3, swift, php-symfony, tizen, typescript-aurelia, typescript-angular, typescript-inversify, typescript-angularjs, typescript-fetch, typescript-jquery, typescript-node, undertow, ze-ph, kotlin-server]
- 查看調用生成代碼指令支持的參數
java -jar swagger-codegen-cli.jar help generate
,執行命令後可以看到有很多的參數以及配置項(輸出結果就不在這裏貼出來了),但我們常用的參數有這麼幾項,大部分的參數都可以單獨配置到一個配置文件中,這個待會兒在下一個節點單獨介紹。常用參數如下:- -i : 指定接口描述文件
- -c : 指定配置項所在的配置文件
- -D : 指定虛擬機參數。例如指定生成代碼的groupId:-D --group-id com.monk.demo
- -l : 指定生成代碼的語言(生成客戶端和服務端代碼的區別就是通過語言的不同來區分的)
- -o : 指定生成代碼的路徑
-
查看將要生成語言的代碼支持的配置參數,這裏以java語言爲例。
java -jar swagger-codegen-cli.jar config-help -l java
,輸出結果就不在這裏貼出來了,大家可以執行下之後看下輸出的內容。我這裏也整理了下,最常用的參數有這些,如下:- apiPackage:生成的api包名
- modelPackage:生成的api包名
- sourceFolder:生成的源碼文件夾。例如/src/main/java
- java8:是否支持java8。默認false
- groupId:生成的mavne項目的座標groupId
- artifactId:生成的mavne項目的座標artifactId
- artifactVersion:生成的mavne項目的座標artifact版本號
- artifactDescription:生成的mavne項目的artifact描述信息
還有一些參數時配置開發者信息、聯繫方式等等無關痛癢的參數,我就不再這裏列出了,如果有需要可以適當的配置進去,各取所需吧
通過swagger-code生成客戶端、服務端代碼
提示: 這一章節只介紹怎麼生成代碼,測試環境見一下章節
通過swagger-code生成客戶端和服務端的代碼,目前我所知道的有三種,分別如下:
- 通過swagger-codegen-cli.jar包生成
- 通過java代碼來生成
- 通過maven-plugin的方式生成
接下來將分別介紹如何通過這三種方式生成代碼:
在開始之前,我們得先準備兩個文件:
-
接口的描述文件(這裏我們直接拿官網提供的接口描述文件來示例,描述文件在**./modules/swagger-codegen/src/test/resources/2_0/petstore.yaml**)。這裏我們將這個文件複製到D:\temp目錄下去,並修改這個文件中的內容,修改的地方如圖所示:
-
生成代碼的公共配置文件(也就是上面**-c**參數需要指定的配置文件)
-
服務端代碼配置文件
文件內容如下:(將其保存爲swagger-server.json,保存在D:\tmep\swagger-server目錄下)
{ "invokerPackage": "com.monk.client", "apiPackage": "com.monk.client.api", "modelPackage": "com.monk.client.bean", "sourceFolder": "src/main/java", "java8": true, "dateLibrary": "java8", "groupId": "com.monk", "artifactId": "swagger-client", "artifactVersion": "1.0.0", "artifactDescription": "This is a demo for generate java client by swagger-codegen." }
-
客戶端代碼配置文件
文件內容如下:(將其保存爲swagger-client.json,保存在D:\tmep\swagger-client目錄下)
{ "invokerPackage": "com.monk.server", "apiPackage": "com.monk.server.api", "modelPackage": "com.monk.server.bean", "configPackage": "com.monk.server.config", "java8": true, "groupId": "com.monk", "artifactId": "swagger-server", "artifactVersion": "1.0.0", "artifactDescription": "This is a demo for generate java server by swagger-codegen." }
-
然後將剛剛我們編譯的swagger-codegen-cli.jar複製到D:\temp目錄下。最終的目錄結構如下圖所示:
通過swagger-codegen-cli.jar包生成
關於swagger-codegen-cli.jar包的使用方法以及參數介紹,在上一章節已經介紹過了,這裏就直接開始生成了,又不懂和疑問的地方,請聯繫我。
生成服務端代碼
指令如下:
java -jar swagger-codegen-cli.jar generate -i .\petstore.yaml -l spring -c .\swagger-server\swagger-server.json -o .\swagger-server
至此,服務端代碼生成成功。
生成客戶端代碼
指令如下:
java -jar swagger-codegen-cli.jar generate -i .\petstore.yaml -l java -c .\swagger-client\swagger-client.json -o .\swagger-client
就這麼簡單,客戶端代碼到目前爲止也生成成功了。
通過java代碼來生成
參見編譯swagger-codegen章節,我們重新編譯下源碼,需要將生成的swagger-codegen-cli.jar安裝到我們maven的本地的倉庫中,然後通過依賴的方式加入到項目中。修改編譯的指令爲mvn clean package install
-
我們新建一個maven項目swagger-codegen-demo,在pom文件中加入以下依賴
<dependency> <groupId>io.swagger</groupId> <artifactId>swagger-codegen-cli</artifactId> <version>2.4.13-SNAPSHOT</version> </dependency>
-
新建測試類,添加main方法,編寫以下代碼
package com.monk; import io.airlift.airline.Cli; import io.airlift.airline.Help; import io.swagger.codegen.cmd.*; /** * Hello world! * */ public class App { public static void main( String[] args ) { Cli.CliBuilder<Runnable> builder = Cli.<Runnable>builder("swagger-codegen-cli") .withDescription( String.format( "Swagger code generator CLI (version %s). More info on swagger.io", "2.4.5")) .withDefaultCommand(Langs.class) .withCommands(Generate.class, Meta.class, Langs.class, Help.class, ConfigHelp.class, Validate.class, Version.class); // 生成客戶端代碼 builder.build().parse(new String[]{ "generate", "-i", "D:\\temp\\petstore.yaml", "-l", "java", "-o", "D:\\temp\\java\\swagger-client", "-c", "D:\\temp\\swagger-client\\swagger-client.json" }).run(); // 生成服務端代碼 builder.build().parse(new String[]{ "generate", "-i", "D:\\temp\\petstore.yaml", "-l", "spring", "-o", "D:\\temp\\java\\swagger-server", "-c", "D:\\temp\\swagger-server\\swagger-server.json" }).run(); } }
-
這裏不難發現,其實和我們調用的jar包的方式生成代碼是一樣的,只不過將命令換一種方式去執行而已。
通過maven-plugin的方式生成
- 接着上一章節來,我們將對應的資源文件添加項目的src/main/resources目錄下;
- 接口描述文件 petstore.yaml
- 客戶端配置文件 swagger-client.json
- 服務端配置文件 swagger-server.json
- 然後修改對應的pom文件
<!-- 添加屬性 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<swagger-server-output-dir>D:\temp\plugin\swagger-server</swagger-server-output-dir>
<swagger-client-output-dir>D:\temp\plugin\swagger-client</swagger-client-output-dir>
<swagger-codengen-input-yml>src/main/resources/petstore.yaml</swagger-codengen-input-yml>
<swagger-client-config-json>src/main/resources/swagger-client.json</swagger-client-config-json>
<swagger-server-config-json>src/main/resources/swagger-server.json</swagger-server-config-json>
</properties>
<build>
<plugins>
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<inputSpec>${swagger-codengen-input-yml}</inputSpec>
<!--生成服務端代碼-->
<!--<language>spring</language>
<output>${swagger-server-output-dir}</output>
<configurationFile>${swagger-server-config-json}</configurationFile>-->
<!-- 生成客戶端代碼 -->
<language>java</language>
<output>${swagger-client-output-dir}</output>
<configurationFile>${swagger-client-config-json}</configurationFile>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
- 藉助maven來生成對應的客戶端喝服務端代碼。
開發並測試我們的服務端以及服務端
下文中的開發和測試,均用PetApi.findPetsByStatus(List status)方法來演示
開發服務端代碼
首先打開剛剛生成的服務端代碼,打開PetApi.java類,找到對應的findPetsByStatus(List status)方法,服務端的邏輯代碼就可以寫在這裏。
下面我就修改生成的代碼,修改其返回值,如圖所示:
由於生成的服務端是一個spring-boot項目,我們可以直接找到Swagger2SpringBoot.java類,右鍵**Run ‘Swagger2SpringBoot’**等待啓動成功即可。
PS:
- 關於springBoot的啓動端口,項目的contextPath怎麼配置及怎麼看,這裏就不再贅述了,請自行百度。
- 又由於是個springBoot項目,我們也可以用maven命令
maven clean package
將項目打成jar包,然後用cmd命令java -jar swager-server.jar
來啓動項目。
開發客戶端代碼
打開剛剛生成的swagger-client項目,有以下幾個地方需要注意一下:
-
打開ApiClient.java類,檢查下私有全局變量basePath是否設置的是服務端的地址
http://localhost:8080
,如果不是的話,就更正爲自己的服務端地址。如圖所示:(正常情況是不會生成錯的,但是還是檢查下,否則會拋出ApiException) -
在生成客戶端代碼的同時,也會爲我們生成一份測試用例,只不過這個測試用例是空的,並且是加了**@Ignore註解的,那麼我們需要簡單的修改一下。和服務端代碼相呼應,我們這裏繼續拿PetApiTest**類來演示,如下所示:
-
首先將類上的**@Ignore**註解去掉
-
完善測試用例方法,如下所示:
@Test public void findPetsByStatusTest() throws ApiException { List<String> status = new ArrayList<String >(); // 如果你看懂了服務端的代碼,就明白這裏填寫任意值是不影響我們的返回結果的,只要不爲空就好 status.add("available"); List<Pet> response = api.findPetsByStatus(status); response.forEach(item->{ System.out.println(item); }); }
-
-
最終的輸出結果如圖所示:
客戶端的調用
我們可以將我們生成的客戶端代碼打包並添加到我們的本地maven倉庫,這樣別人就可以直接通過添加這個jar的依賴就可以完成對服務端的調用。
-
使用maven指令將swagger-client打包到本地maven倉庫
mvn clean install -Dmaven.test.skip=true
-
打開上面新建的測試項目swagger-codegen-demo,添加以下依賴:
<dependency> <groupId>com.monk</groupId> <artifactId>swagger-client</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
-
新建測試類TestClientApi,編寫測試方法testApi(),如下所示:
@Test public void testApi() throws ApiException { PetApi api = new PetApi(); List<String> status = new ArrayList<String >(); // 如果你看懂了服務端的代碼,就明白這裏填寫任意值是不影響我們的返回結果的,只要不爲空就好 status.add("available"); List<Pet> response = api.findPetsByStatus(status); response.forEach(item->{ System.out.println(item); }); }
-
輸出結果如圖所示:
總結
通過三種生成客戶端、服務端代碼方式的比較,生成代碼的方式都是圍繞着swagger-codegen-cli這個jar包轉,除開maven-plugin的方式一時想不到合適的應用場景外,我覺得另外兩個方式各自有各自的場景。比方說:我們在實際項目中根據接口描述文檔開發的時候就可以用jar包生成的方式;而用java代碼的方式生成的話,我覺得可以集成我們的管控平臺上,就可以像sawgger官方的編輯器一樣了,管控一鍵生成接口設計文檔,然後再根據設計文檔生成對應的客戶端代碼、服務端代碼,將代碼打包提供下載即可。
通篇看完,估計你也在疑惑,爲什麼指定的language不同,理論上spring也是java呀,java生成的就是客戶端,spring生成的就是服務端。開始我也很納悶,甚至在看github的官方文檔的時候,關於生成客戶端代碼,花了大篇幅來簡述怎麼生成客戶端代碼,可以參見To generate a sample client library,而怎麼生成服務端代碼就一句話,參見To build a server stub,然而這句話還是一句超鏈接,跳到了另外一個地方,如圖所示:
他在這裏列舉了一些服務端代碼怎麼生成,大家也可以自行去瀏覽一下https://github.com/swagger-api/swagger-codegen/wiki/Server-stub-generator-HOWTO