根据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常用指令
以下指令需要在swagger-codegen-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