如何在 Grails 中使用 Swagger 来编写 API 文档

16 篇文章 0 订阅
11 篇文章 0 订阅

一、序

用 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 文档,使用非常地简单(当然目前功能也是非常简单)。
主要功能特点是:

  1. 支持OpenAPI v3(最新的规范版本,2020年)规范
  2. 学习轻松,使用极其方便,只需要记住一个注解 @ApiDoc,就能用在所有关于 api 文档的地方,它的注解成员和 OpenAPI v3 中的一个个独立注解一一对应,指令也一一对应。
  3. 可以自动从 POJO/Command/任意类 生成Schema,会自动抽取注释,而无需给每个属性添加注解,只需要给类添加一个注解即可,属性的注解就能自动成为 Schema 属性的描述内容(description),当然也可以用 @ApiDoc 来重新定义 API 文档中的描述内容。
  4. 提供在线查看界面,地址是 http://<your.domain>/api
  5. 提供JSON格式的 API 文档对象,地址是 http://<your.domain>/api/doc
  6. 已经开源,MIT协议,大公无私,实在是二次开发、改进、商用之良心协议
  7. GIT 地址是 https://github.com/yangbo/swagger-grails4

喜欢项目的话,记得点击 git hub 上的 “STAR” 啊。

使用插件注意事项

  1. grails版本需要大于 v4.0.0
  2. 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

扩展 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

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
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 简介 2. 起步 2.1 下载并安装Grails 2.2 创建一个Grails应用 2.3 Hello World示例 2.4 使用IDE 2.5 规约配置 2.6 运行Grails应用 2.7 测试Grails应用 2.8 部署Grails应用 2.9 所支持的Java EE容器 2.10 创建工件 2.11 生成Grails应用 3. 配置 3.1 基本配置 3.1.1 内置选项 3.1.2 日志 3.2 环境 3.3 数据源 3.3.1 数据源和环境 3.3.2 JNDI数据源 3.3.3 自动数据库移植 3.4 外部配置 3.5 定义版本 4. 命令行 4.1 创建Gant脚本 4.2 可复用的Grails脚本 4.3 脚本的事件 4.4 Ant和Maven 5. 对象关系映射(GORM) 5.1 快速指南 5.1.1 基本的CRUD 5.2 在GORM进行领域建模 5.2.1 GORM的关联 5.2.1.1 一对一 5.2.1.2 一对多 5.2.1.3 多对多 5.2.2 GORM的组合 5.2.3 GORM的继承 5.2.4 集合、列表和映射 5.3 持久化基础 5.3.1 保存和更新 5.3.2 删除对象 5.3.3 级联更新和删除 5.3.4 立即加载和延迟加载 5.3.4 悲观锁和乐观锁 5.4 GORM查询 5.4.1 动态查找器 5.4.2 条件查询 5.4.3 Hibernate查询语言 5.5 高级GORM特性 5.5.1 事件和自动实现时间戳 5.5.2 自定义ORM映射 5.5.2.1 表名和列名 5.5.2.2 缓存策略 5.5.2.3 继承策略 5.5.2.4 自定义数据库标识符 5.5.2.5 复合主键 5.5.2.6 数据库索引 5.5.2.7 乐观锁和版本定义 5.5.2.8 立即加载和延迟加载 5.6 事务编程 5.7 GORM和约束 6. Web层 6.1 控制器 6.1.1 理解控制器和操作 6.1.2 控制器和作用域 6.1.3 模型和视图 6.1.4 重定向和链 6.1.5 控制器拦截器 6.1.6 数据绑定 6.1.7 XML和JSON响应 6.1.8 上传文件 6.1.9 命令对象 6.2 Groovy Server Pages 6.2.1 GSP基础 6.2.1.1 变量和作用域 6.2.1.2 逻辑和迭代 6.2.1.3 页面指令 6.2.1.4 表达式 6.2.2 GSP标签 6.2.2.1 变量和作用域 6.2.2.2 逻辑和迭代 6.2.2.3 搜索和过滤 6.2.2.4 链接和资源 6.2.2.5 表单和字段 6.2.2.6 标签作为方法调用 6.2.3 视图和模板 6.2.4 使用Sitemesh布局 6.3 标签库 6.3.1 简单标签 6.3.2 逻辑标签 6.3.3 迭代标签 6.3.4 标签命名空间 6.4 URL映射 6.4.1 映射到控制器和操作 6.4.2 嵌入式变量 6.4.3 映射到视图 6.4.4 映射到响应代码 6.4.5 映射到HTTP方法 6.4.6 映射通配符 6.4.7 自动重写链接 6.4.8 应用约束 6.5 Web Flow 6.5.1 开始和结束状态 6.5.2 操作状态和视图状态 6.5.3 流执行事件 6.5.4 流的作用域 6.5.5 数据绑定和验证 6.5.6 子流程和会话 6.6 过滤器 6.6.1 应用过滤器 6.6.2 过滤器的类型 6.6.3 过滤器的功能 6.7 Ajax 6.7.1 用Prototype实现Ajax 6.7.1.1 异步链接 6.7.1.2 更新内容 6.7.1.3 异步表单提交 6.7.1.4 Ajax事件 6.7.2 用Dojo实现Ajax 6.7.3 用GWT实现Ajax 6.7.4 服务端的Ajax 6.8 内容协商 7. 验证 7.1 声明约束 7.2 验证约束 7.3 客户端验证 7.4 验证和国际化 8. 服务层 8.1 声明式事务 8.2 服务的作用域 8.3 依赖注入和服务 8.4 使用Java的服务 9. 测试 9.1 单元测试 9.2 集成测试 9.3 功能测试 10. 国际化 10.1 理解信息绑定 10.2 改变Locales 10.3 读取信息 11. 安全 11.1 预防攻击 11.2 字符串的编码和解码 11.3 身份验证 11.4 关于安全的插件 11.4.1 Acegi 11.4.2 JSecurity 12 插件 12.1 创建和安装插件 12.2 理解插件的结构 12.3 提供基础的工件 12.4 评估规约 12.5 参与构建事件 12.6 参与运行时配置 12.7 运行时添加动态方法 12.8 参与自动重载 12.9 理解插件加载的顺序 13. Web服务 13.1 REST 13.2 SOAP 13.3 RSS和Atom 14. Grails和Spring 14.1 Grails的支柱 14.2 配置其他Bean 14.3 通过Beans DSL运行Spring 14.4 配置属性占位 14.5 配置属性重载 15. Grails和Hibernate 15.1 通过Hibernate注释进行映射 15.2 深入了解 16. 脚手架

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值