目录
- 一、序
- 二、试用 swagger-plugin (已经证实在Grails4中不能工作,没时间的可以跳过了)
- 三、必须自己开发一个 Grails4 Swagger 插件了
- 四、自研Grails4 Swagger 插件的进展
- 五、开发 Grails4 Swagger(OpenAPI v3) 插件的技术笔记
- Swagger 插件的实现原理
- 理解 Swagger 所使用的规范 OpenAPI 3
- 开发一个Grails4 的 swagger 插件 swagger-grails4-plugin
- 如何完全控制 OAS API 的定义
- 扩展 Swagger
- 可视化和交互
- groovy 无法使用复杂的 annotation,但可以用更好的“闭包”注解参数
- 需要仿造 jaxrs2 reader 写一个 grails 的 reader
- OpenAPI 的模型
- 使用 builder strategy 来实现OpenAPI文档DSL
- 需要读取 grails 的 controller 类以及 action 和 url mapping
- 访问 url mapping 的方法
- Groovy 中查找 method 的方式
- Groovy 的 annotation closure 不能和 @CompileStatic 一起使用
- 使用 SpringDoc 来展示 OpenAPI json 文档
- 将 swagger-ui 集成到 swagger-grails4-plugin 中
一、序
用 Grails 开发 REST API 时,需要快速、方便地为 REST API 创建文档,以便提供给前端工程师查看。
SpringMVC 中最流行的API工具是 swagger,虽然也不怎么样好用、且语法臃肿,但 swagger 可以自动生成一个在线文档,比较“离线文档”的模式要方便一些。
目前实用了一个离线生成 api 文档的工具,叫 apidoc ,生成的api文档还算美观,但是缺点是编写文档时比较繁琐,生成需要执行额外的命令。
看到 Grails 有一个 swagger 插件,于是决定试一试看。这个插件之前面向的是 Grails3,不清楚对 Grails4 是否一样支持。
二、试用 swagger-plugin (已经证实在Grails4中不能工作,没时间的可以跳过了)
问题1
按照要求添加了依赖、配置后,准备在 controller 上添加 @Api 注解,却报告无法找到 @Api 注解的编译错误,但查看 gradle 依赖,的确已经包含了 swagger-annotations-1.5.22.jar 。这是为什么呢?
原来是通过 plugin 声明的 swagger 依赖并没有生效,需要在项目中显式地声明对 swagger 的依赖,添加下面的依赖后问题解决。
dependencies {
// swagger
compile 'org.grails.plugins:swagger:1.0.1'
compile 'io.swagger:swagger-annotations:1.5.22'
}
其中 swagger-annotations 本来是不需要的,但不知道什么原因,必须显式地添加,否则找不到注解。
问题2
用 grails 命令启动应用时报告下面错误:
Error |
Error occurred running Grails CLI: startup failed:
script1594715079000509222335.groovy: 1: unable to resolve class io.swagger.models.Scheme
@ line 1, column 1.
import io.swagger.models.Scheme
^
这个是因为在 conf/application.groovy 脚本中导入了类 Scheme 而 grails 又无法找到该 jar 包导致的。
去掉这个配置就可以了。
问题3
找不到 swagger 文档地址
clone grails3 swagger 示例后,可以用 gradlew.bat assemble 编译出一个 war,放到 tomcat 下去运行。
然后访问 http://localhost:9090/swagger-example/apidoc/getDocuments 来打开 swagger 页面。
其实 swagger 页面还真的不怎么好看和好用。
三、必须自己开发一个 Grails4 Swagger 插件了
研究了已有的 swagger grails 插件,发现他们都无法在 grails 4 中正常工作,并且使用的 OpenAPI 规范都太旧,全部是 swagger2 的。
目前最新的 Swagger 是 v3 即 OpenAPI v3,因此我们需要自研一个 Grails4 Swagger3 插件了。
四、自研Grails4 Swagger 插件的进展
先说下进展,为Grails社区贡献一点力量吧。
已经成功开发了一个 grails 插件,能自动生成 swagger OASv3 文档,使用非常地简单(当然目前功能也是非常简单)。
主要功能特点是:
- 支持OpenAPI v3(最新的规范版本,2020年)规范
- 学习轻松,使用极其方便,只需要记住一个注解 @ApiDoc,就能用在所有关于 api 文档的地方,它的注解成员和 OpenAPI v3 中的一个个独立注解一一对应,指令也一一对应。
- 可以自动从 POJO/Command/任意类 生成Schema,会自动抽取注释,而无需给每个属性添加注解,只需要给类添加一个注解即可,属性的注解就能自动成为 Schema 属性的描述内容(description),当然也可以用 @ApiDoc 来重新定义 API 文档中的描述内容。
- 提供在线查看界面,地址是 http://<your.domain>/api
- 提供JSON格式的 API 文档对象,地址是 http://<your.domain>/api/doc
- 已经开源,MIT协议,大公无私,实在是二次开发、改进、商用之良心协议
- GIT 地址是 https://github.com/yangbo/swagger-grails4
喜欢项目的话,记得点击 git hub 上的 “STAR” 啊。
使用插件注意事项
- grails版本需要大于 v4.0.0
- applicaiton 中需要添加 plugin jar 的依赖
五、开发 Grails4 Swagger(OpenAPI v3) 插件的技术笔记
Swagger 插件的实现原理
主要步骤是用 swagger 的工具类生成 api json 描述,当然是符合 swagger api 规范定义的 json 文件,然后由前端 swagger-ui 进行渲染显示。
主要类是 SwaggerService 和 ApiDocController。
理解 Swagger 所使用的规范 OpenAPI 3
这里有一篇文章,说明了 OpenAPI 2 和 3 的区别。
OpenAPI3 的注解示例。
示例2,有 通用信息 和 服务器 定义的示例,代码如下:
@OpenAPIDefinition(
info = @Info(
title = "Order API",
version = "v2",
description = "This app provides REST APIs for order entities",
contact = @Contact(
name = "Raymond",
email = "admin@orderapi.com",
url = "http://orderapi.com/support"
)
),
),
servers = {
@Server(
url="<dev url>",
description="DEV Server"
),
@Server(
url="<prod url>",
description="PROD Server"
)
}
)
public class OpenApiConfig {
}
讲解 OAS v3 规范的文档,这个文档对规范进行了详细说明。
开发一个Grails4 的 swagger 插件 swagger-grails4-plugin
插件可以自动生成 swagger api json 定义文档,但不只是使用swagger的注解,因为注解太繁琐了,而是利用 grails 强大的反射功能,自动从 url-mapping、controller、action 等类和注释中自动生成 api 文档,,同时提供修改默认定义的方法,提供足够的灵活性。
需要用到 swagger-core
Swagger Core 2.X produces OpenApi 3.0 definition files.
先阅读 Swagger 2.x getting started
,这个文档重要,说明了 swagger-core 的主要用法。
swagger-core is an open source Java implementation of Swagger/OpenAPI, providing:
swagger-models: OpenAPI specification Java implementation
swagger-core: resolves (annotated) java POJOs into OpenAPI schemas, handles serialization/deserialization and provides an integration mechanism.
swagger-jaxrs2: resolves JAX-RS (annotated) resources into an OpenAPI definition, and provides an integration mechanism.
swagger-annotations: a set of annotations to declare and manipultate output generated by swagger-core, swagger-jaxrs2 and/or other projects.
swagger-maven-plugin (since 2.0.5): provides a maven plugin to resolve an OpenAPI definition at build time (using swagger-jaxrs2). Please see module readme
swagger-gradle-plugin (since 2.0.5): provides a gradle plugin to resolve an OpenAPI definition at build time (using swagger-jaxrs2). Please see module readme
在 JAX-RS 应用中使用 sawagger-core 的方法:
Just by adding the dependencies, an endpoint <server_url>/<application_path>/openapi.json (and openapi.yaml) is activated, exposing the OpenAPI definition of the app APIs serialized as json or yaml, as resolved by swagger-core processing JAX-RS resources defined in the application.
可以把 openapi.yaml 文件放在 classpath 中来配置应用。
如何完全控制 OAS API 的定义
To handle this and other cases, and to be able to have full control over the resolved API definition, usage of Swagger annotations comes handy. Further customization can also be achieved by extension mechanisms.
扩展 Swagger
- Reader listeners
- Filters
- Configure a custom scanner
- Extending Reader — 生成OpenAPI定义
- Extending core Resolver — 解析程序中的类为 OpenAPI 中的 schemas(结构),例如响应对象、请求体、参数等的定义。
可视化和交互
用 swagger-ui 来提供。
groovy 无法使用复杂的 annotation,但可以用更好的“闭包”注解参数
Closure annotation parameters
需要仿造 jaxrs2 reader 写一个 grails 的 reader
jaxrs2 的reader 实现
https://github.com/swagger-api/swagger-core/blob/42b52cccd9c7aa5a4cda5e3260283dddf4348364/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java#L71
reader 的测试类,对理解Reader的功能有帮助
https://github.com/swagger-api/swagger-core/blob/master/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java
OpenAPI 的模型
一个API文档,有一个 OpenAPI 对象,它下面有一个 Paths 对象,这个 Paths 对象是一个 Map,存放路径与路径对象的对应关系。
需要熟悉 OpenAPI Specification v3
https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md
原来 swagger 中的 action 分组(即属于哪个controller)是由 tags 来对应和确定的。在根节点的 tags 属性中定义本 api 文档的所有分组(controller),json 如下:
然后在 paths 中用 tags 来与上面的分类对应,代码如下:
使用 builder strategy 来实现OpenAPI文档DSL
需要读取 grails 的 controller 类以及 action 和 url mapping
需要使用 Artifact API 来访问 controller 和 url-mapping 信息。
主要是 GrailsApplication 类提供了访问 grails 组件(Artifacts) 的方法。
取到的“组件”对象是 GrailsClass 类实例。
访问 url mapping 的方法
入口类是 UrlMappingsHolder,需要从 spring 上下文中,用 bean name ‘grailsUrlMappingsHolder’ 来获取,代码如下:
urlMappingsHolder = applicationContext.getBean("grailsUrlMappingsHolder", UrlMappingsHolder)
而 url mapping 信息都放在 UrlMapping 类中了。
要看 allowedMethods :
static allowedMethods = [action1:'POST',
action3:['POST', 'DELETE']]
还可以用 LinkGenerator 类。
Groovy 中查找 method 的方式
如果需要支持 meta-programming 即动态添加 method、属性,那么可以用下面的方法:
def method obj.metaClass.methods[0]
method.cachedMethod.declaredAnnotations
method.getAnnotation(Class<T> annotationClass)
Groovy 的 annotation closure 不能和 @CompileStatic 一起使用
annotation closure 会导致 @CompileStatic 报告类型检查失败。
意味着所有添加了 @ApiDoc 的 action 方法都不能是 @CompileStatic 的,导致性能下降。
使用 SpringDoc 来展示 OpenAPI json 文档
https://springdoc.org/
看了一下,不行,还是用 swagger-ui 原始项目来做集成比较好。
将 swagger-ui 集成到 swagger-grails4-plugin 中
用 webpack 进行打包,将打包后的 js 做为静态资源 assets 对外提供。
成功将 swagger js 集成进 plugin,但是 css 不知道如何集成,所以选择了用 swagger-dist 来整体集成。
参考:
- https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/installation.md
- https://github.com/swagger-api/swagger-ui/tree/master/docs/samples/webpack-getting-started