DEVOPS实现Swagger2应用接口文档Word格式的在线实时更新
借助 maven plugin 自定义服务Swagger2Word服务,实现满足自定义格式需求的RESTful API文档的实时更新和在线展示。(注意:这里的API接口文档是独立于应用的,不似Swagger那样必须启动应用才能看到相应文档。)
1. 前言
通过文档自动化地实时在线更新, 杜绝低级人工错误,实现引入错误的快速发现,降低双方的沟通摩擦成本,加速开发效率。
2. 效果图
现阶段我们会一次性生成word,html两种文件类型的接口文档。其中word格式的提供给公司外部的系统对接方,html格式的则提供内部的实时在线查看。文档内容格式如下(尚未经过设计,凑活看):
3. 实现
我们需要使用到三个Maven Plugin,以及一个自定义的服务。
3.1 Swagger2.0 格式规范文件的生成
在这一步我们需要借助Maven Plugin之 swagger-maven-plugin
,实现在Maven的 Compile阶段,基于项目源码中的Swagger注解生成符合Swagger2.0格式规范的JSON文件。( Swagger2.0格式规范具体要求参见 Swagger2.0文档格式规范 )
这一步最大的难点是针对不同版本的swagger-maven-plugin
,其配置上略有差异,因此这里笔者直接给出明确的版本号(其实SpringBoot的 parent 依赖是设置了默认版本号的),其它细节参见下方源码。
<!-- Swagger注解导出为Word文档 开始 -->
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>3.1.8</version>
<configuration>
<apiSources>
<apiSource>
<!-- Tell the plugin your project is a JAX-RS(false) or a SpringMvc(true) project-->
<springmvc>true</springmvc>
<locations>
<!-- required-->
<!-- Classes containing Swagger's annotation @Api,
or packages containing those classes can be configured here.
Each item must be located inside a tag-->
<location>org.xx.xx.xx.controller</location>
</locations>
<!-- 这一步为扩展支持knife4j, 具体作用参见下面的小节 -->
<swaggerExtensions>
<swaggerExtension>com.github.kongchen.swagger.docgen.extend.Knife4jApiOperationSupportVendorExtension</swaggerExtension>
</swaggerExtensions>
<schemes>
<scheme>http</scheme>
<scheme>https</scheme>
</schemes>
<host></host>
<basePath></basePath>
<!-- <typesToSkip></typesToSkip> -->
<info>
<!--required-->
<title>title</title>
<version>v1</version>
<description>description</description>
<termsOfService>
http://www.github.com/kongchen/swagger-maven-plugin
</termsOfService>
<contact>
<email>x</email>
<name>x</name>
<url>x</url>
</contact>
<license>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<name>Apache 2.0</name>
</license>
</info>
<outputFormats>json</outputFormats>
<!--以项目根目录为基础 -->
<swaggerDirectory>generated/swagger-ui</swaggerDirectory>
<!-- 这里我们使用自定义模板 -->
<!-- Support classpath or file absolute path here. 1) classpath e.g: "classpath:/markdown.hbs", "classpath:/templates/hello.html" 2) file e.g: "${basedir}/src/main/resources/markdown.hbs", "${basedir}/src/main/resources/template/hello.html"
<templatePath>${basedir}/templates/strapdown.html.hbs</templatePath>
<outputPath>${basedir}/generated/document.html</outputPath> -->
</apiSource>
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
3.1.1 实现扩展 - 支持knife4j
以下以knife4j中的注解ApiOperationSupport
为例,介绍如何借助swagger-maven-plugin
提供的扩展机制,支持API排序功能。
正式开始前,让我们介绍下,截至目前,最新的swagger-maven-plugin
版本号为3.1.9-SNAPSHOT, 其提供的扩展机制主要为如下三种:
- 自定义实现
ClassSwaggerReader
。 - 自定义
SwaggerExtension
实现类。 - 自定义
SwaggerSpecFilter
实现类。 (不过在这个实现类里,你无法拿到注解所修饰的那个原始Method信息)
相关的配置如下:
<apiSource>
<!--1. 实现com.github.kongchen.swagger.docgen.reader.ClassSwaggerReader 接口-->
<!-- 范例参见: 官方单元测试代码中的 VendorExtensionsSpringMvcReader 类 -->
<swaggerApiReader>com.wordnik.swagger.jaxrs.reader.JaxrsReader</swaggerApiReader>
<!--2. 实现 io.swagger.jaxrs.ext.SwaggerExtension 接口 ; 参见文档 https://gitee.com/lqzkcx3/swagger-maven-plugin#a-sample-configuration -->
<!-- 范例参见: 官方单元测试代码中的 TestVendorExtension 类 -->
<swaggerExtensions>
<swaggerExtension>com.example.VendorExtension</swaggerExtension>
</swaggerExtensions>
<!-- 3. 实现 io.swagger.core.filter.SwaggerSpecFilter接口 ; 这一个在官方给出的源码种并没有显式说明, 但确实可用 -->
<!-- 源码中的应用位置: AbstractDocumentSource.doFilter(Swagger swagger) -->
<swaggerInternalFilter>xxxx</swaggerInternalFilter>
</apiSource>
针对注解的支持,我们只需要提供如下使用类:
public class Knife4jApiOperationSupportVendorExtension extends AbstractSwaggerExtension {
@Override
public void decorateOperation(final Operation operation, final Method method, final Iterator<SwaggerExtension> chain) {
final ApiOperationSupport annotation = method.getAnnotation(ApiOperationSupport.class);
if (annotation != null) {
operation.setVendorExtension("x-order", annotation.order());
operation.setVendorExtension("x-author", annotation.author());
}
super.decorateOperation(operation, method, chain);
}
}
然后将以上扩展类进行注册即可,相关配置形式上面已经给出。
最终效果是在生成的swagger.json文件中,新增了x-order,x-author两个扩展属性值,这样我们就可以在之后的word生成过程中,针对这两个扩展出来的字段进行自定义处理,比如排序。
补充几点:
- 如果需要隐藏API,使用注解
@ApiOperation(value = "ignore", hidden=true)
, 注意@ApiIgnore
是无效的。 - 如果Controller方法注解使用:
@RequestMapping
必须标识method 例如@RequestMapping(value = "/getCount", method = RequestMethod.POST)
,如果不标识,就不会生成该接口文档。也可以使用@PostMapping
注解。 - 关于
@ApiIgnore
,建议在SpringMvcApiReader.parseMethod
和SpringMvcApiReader.read(Set<Class<?>> classes)
中扩展。
3.2 生成Word文档
借助我们自定义的RESTFul服务,将上一步生成的JSON文件转换为自定义内容格式的Word / HTML文件(其中HTML格式就是用作在线更新的)。这一步将使用到Maven Plugin之exec-maven-plugin
。
这一步原本笔者初步的想法是实现一个自定义Maven Plugin,但在开始时突然福至心灵地想到为啥不借鉴微服务的思路,这样的优势:
- 可以实现服务功能的单一,使得维护更新方便。过往我们一直试图打造一个统一平台,将内部需要的功能整合进一个应用里,这到底服务更新和代码同步经常出现问题,而且因为需求各异,各类依赖集成在一起之后很容易出现冲突,使得开发环境下应用的启动调试都心惊胆颤,这会打击开发人员的积极性。
- 提升相关人员的Owner感觉,确保了需求的响应速度和质量。过往的大应用服务形式,相关子服务的编写者很容易表现出听之任之的态度。而微服务化拆分服务之后,自主权的提升能够显著提高人员的积极性。(除了接口规范,我们不会限制语言和第三方依赖种类)
- 积累微服务相关的实践经验。作为基础设施极度缺乏,独立部署需求强烈的业务型软件公司,贸然追逐微服务的时髦只会导致各方的苦不堪言,而通过实现内部服务的微服务化,我们可以在实践中去感受和积累微服务的相关经验,探索出适合自己公司业务特点的微服务实战技术路线。
闲话扯远了,咱们拉回来,以下是exec-maven-plugin
的相关配置:
<!-- Swagger注解导出为Word文档 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>generateSwaggerWordFile</id>
<phase>pre-package</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>
org.xx.xx.MavenExecPluginGenerateSwaggerWordFile
</mainClass>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
<!-- -->
<addResourcesToClasspath>
true
</addResourcesToClasspath>
<addOutputToClasspath>true</addOutputToClasspath>
<arguments>
<!-- // 程序的命令行参数 // -->
<!-- 构建目录,缺省为target -->
<argument>${project.build.directory}</argument>
<argument>${session.executionRootDirectory}</argument>
<!-- -->
<argument>${project.groupId}</argument>
<argument>${project.artifactId}</argument>
<argument>${project.version}</argument>
<argument>${project.packaging}</argument>
<!-- swagger文档生成相关 -->
<argument>generated/swagger-ui/swagger.json</argument>
<argument>http://xxx.xx.x.x:9527/strToWord</argument>
<!-- 这是classpath属性,其值就是下面的<classpath/> -->
<argument>-classpath</argument>
</arguments>
</configuration>
</plugin>
相关Java代码:
// ==== MavenExecPluginGenerateSwaggerWordFile.java核心代码
public static void main(String[] args) {
if (args.length < 5) {
output("缺少必要参数, 不执行 MavenExecPluginGenerateMetaFile.java");
return;
}
// debugInputParams(args);
MavenProjectEntity entity = readArgs(args);
final String swaggerJsonFileRelativePath = args[6];
final String swagger2WordServerUrl = args[7];
final String swaggerJsonContent = FileUtil.readString(
FilenameUtil.concat(entity.getExecutionRootDirectory(), swaggerJsonFileRelativePath),
CharsetUtil.CHARSET_UTF_8);
final File wordFileGenerated = FileUtil
.file(FilenameUtil.concat(entity.getExecutionRootDirectory(), swaggerJsonFileRelativePath ".doc"));
// 请求远程RESTful服务,转换JSON文件
HttpRequest.post(swagger2WordServerUrl).queryMap(Collections.singletonMap("jsonStr", swaggerJsonContent))//
.connectTimeout(Duration.ofSeconds(6000))//
.readTimeout(Duration.ofSeconds(6000))//
.writeTimeout(Duration.ofSeconds(6000))//
.execute() //
.onFailed((request, ex) -> Console.error("发生异常:" ex))//
.onSuccess(s -> s.rawBody(t -> FileUtil.writeFromStream(t.byteStream(), wordFileGenerated)));
// 同时生成一份html文件
FileUtil.copy(wordFileGenerated, FileUtil.file(FilenameUtil.concat(entity.getExecutionRootDirectory(),
FilenameUtil.getFullPath(swaggerJsonFileRelativePath), "xxx-yyy.html")), true);
output("接口word文档生成完成!路径为: " wordFileGenerated.getAbsolutePath());
}
static MavenProjectEntity readArgs(String[] args) {
MavenProjectEntity entity = new MavenProjectEntity();
entity.setProjectBuildDirectory(args[0]);
entity.setExecutionRootDirectory(args[1]);
entity.setGroupId(args[2]);
entity.setArtifactId(args[3]);
entity.setVersion(args[4]);
entity.setPacking(args[5]);
return entity;
}
3.3 上传文档
最后就只剩下将生成的HTML格式的接口文档推送到早已部署好的文件服务器指定目录下。这一步我们需要使用到Maven Plugin之wagon-maven-plugin
。
相关源码如下:
<!--上传到在线文件服务器-->
<!-- https://www.cnblogs.com/tqyysm/articles/9815092.html -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
</dependencies>
<configuration>
<serverId>txYunServer</serverId>
<!-- 需要部署的文件 -->
<fromFile>generated/swagger-ui/xxx-yyy.html</fromFile>
<!-- 部署目录 用户:密码@ip 部署地址:端口 root:password@ -->
<url>scp://xxx.yy.zzz.24/usr/local/docker-nginx/html</url>
<commands>
<!-- <command>pkill java</command> -->
<!-- 以天为单位对文档进行更新 -->
<command><![CDATA[mv /usr/local/docker-nginx/html/kanq-authcenter.html /usr/local/docker-nginx/html/kanq-authcenter-`date %Y-%m-%d`.html]]></command>
</commands>
<!-- 显示运行命令的输出结果 -->
<displayCommandOutputs>true</displayCommandOutputs>
</configuration>
</plugin>
3.4 执行完整操作
以上配置完成之后,接下来要做的就是在命令行依次敲出如下指令并回车:
cd {项目根目录}
# 生成swagger2.0 JSON文件
mvn swagger:generate
# 生成 word/html自定义内容格式文档
mvn exec:java
# 将html格式的文档推送到静态文件服务器
mvn wagon:upload-single wagon:sshexec
###### 将以上命令组装起来
mvn swagger:generate exec:java wagon:upload-single wagon:sshexec
4. 补充 - 搭建静态文件服务器
秉承送佛送到西,以下也给出笔者在使用docker搭建nginx静态文件服务器的相关命令和配置。
##### 宿主机上需要准备的nginx配置文件
# /usr/local/docker-nginx目录下添加如下文件(内容参见本小节尾部):
nginx.conf
conf.d/default.conf
docker run --name nginx_container -p 9527:80 -d -e TZ="Asia/Shanghai" -v /etc/localtime:/etc/localtime:ro -v /usr/local/docker-nginx/html:/usr/share/nginx/html -v /usr/local/docker-nginx/nginx.conf:/etc/nginx/nginx.conf:ro -v /usr/local/docker-nginx/conf.d/:/etc/nginx/conf.d/ -v /usr/local/docker-nginx/logs:/var/log/nginx nginx
###################################### default.conf内容
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
autoindex on;
autoindex_exact_size off; #设置以MB、GB等单位显示文件大小
autoindex_localtime on; #设置显示目录或文件的时间属性
alias /usr/share/nginx/html/;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
###################################### nginx.conf内容
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
5. 补充 - 集成到DEVOPS流程
其实上面的2.4小节里已经给出了思路,具体嵌入到哪里就看读者所在公司的自动化进度以及流程特点了。
6. 罗嗦几句
通过全自动的文档生成,促进研发过程中定期审查机制的执行,检查人无需启动相关应用,即可检查应用的基本情况 —— 注解说明是否完备,设计是否合理/被违背等。(这其实还是自动化程度不够导致系统可能启动非常麻烦),定期的检查远远好过毕其功于一役的最终的一次性测试和检查,把功夫做在平时。
7. 参考
- GitHub - Swagger2Word 我们就是基于这个开源项目实现的Swagger注解转Word文档的。
- Knife4jApiOperationSupportVendorExtension 。没有必要引入笔者这个JAR,只需要引入这个类即可。
- swagger-maven-plugin - 夫礼者
- swagger-maven-plugin另外一个扩展范例
- 扩展 swagger支持文档自动列举所有枚举值
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfkbgec
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13