![Go语言编程之旅:一起用Go做项目](https://wfqqreader-1252317822.image.myqcloud.com/cover/485/32441485/b_32441485.jpg)
2.4 接口文档
前面完成了对业务需求模块和路由的设计,并且处理了公共组件,初步运行也没有问题,那么现在是不是就可以开始编码了呢?
其实不然,虽然我们完成了路由的设计,但是接口的定义并不是一个人的事。我们在提前设计好接口的入参、出参及异常情况后,还需要与其他同事一起进行接口设计评审,以便确认本次迭代的接口设计方案是尽可能正确和共同认可的,如图2-6所示。
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_85_1.jpg?sign=1740111459-jOW47rxiOXQ7SgU3Uy8T7H6slux0l5ZQ-0-996c858ee81abfb1740666cd3d317d10)
图2-6
2.4.1 Swagger简介
如何维护接口文档是绝大部分开发人员都遇到过的问题,因为前端开发人员、后端开发人员、测试开发人员等都要看,如果每个人都给一份,如何维护将成为一个非常大的问题。在很多年以前,流行过用 Word 等工具写接口文档,显然这会产生许多问题,后端开发人员将耗巨大的精力,文档的时效性无法得到保障。
针对这类问题,市场上出现了大量的解决方案,Swagger 是其中的佼佼者。它更加的全面和完善,具有相关联的生态圈,是基于标准的OpenAPI规范进行设计的。只要按照这套规范编写注解或通过扫描代码生成注解,就能生成统一标准的接口文档和一系列Swagger工具。
2.4.2 OpenAPI和Swagger
前面曾提到OpenAPI,那么OpenAPI和Swagger之间是什么关系呢?
其实OpenAPI规范是在2015年由OpenAPI Initiative捐赠给Linux基金会的,并且Swagger对此更进一步地对OpenAPI规范提供了大量与之相匹配的工具集,能够充分利用OpenAPI规范映射生成所有与之关联的资源,并且查看和调用RESTful接口,因此Swagger不仅是一个“规范”,更是一个框架。
从功能上来讲,OpenAPI规范能够帮助我们描述一个API的基本信息,比如:
● 有关该API的描述。
● 可用路径(或资源)。
● 在每个路径上的可用操作(获取和提交等)。
● 每个操作的输入和输出格式。
2.4.3 安装 Swagger
Swagger工具集可根据OpenAPI规范生成各类与接口相关联的工具,常见的流程是:编写注解→调用生成库→生成标准描述文件→生成/导入对应的 Swagger 工具。因此需要先安装 Go对应的开源Swagger相关联的库,即在项目blog-service的根目录下执行安装命令:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_86_1.jpg?sign=1740111459-SqztuAaNxLQurHwouDlFeqZq21I3B3XS-0-7e5c5ef01656c3a2041dd27890966613)
然后验证是否安装成功:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_86_2.jpg?sign=1740111459-BVXOSLAAFogsfZY0er7iSzrSPo3X9FuU-0-51183f3b7efd57c448e79e8dd2ef422c)
如果命令行提示找不到swag文件,则可以检查一下对应的bin目录是否已经加入环境变量PATH中。
2.4.4 写入注解
在安装完Swagger关联库后,就需要项目里的API接口编写注解,以便后续在生成时能够正确地运行。本节将用到的注解如表2-3所示。
表2-3
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_86_3.jpg?sign=1740111459-sMnGcIpPX611vCawMpX4i78KXl8fBLKK-0-3a66e60d400c792f1625a373c1bca6b6)
1.API
切换到项目目录下的internal/routers/api/v1目录中,打开tag.go文件,写入如下注解:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_86_4.jpg?sign=1740111459-M7V9nhCM8drAj77hnqFNxHysjIF5C6yK-0-74276a84a9283c038dccb7b7627e5229)
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_87_1.jpg?sign=1740111459-OQPgLUpvWpUoQXjOq2GbtZjN70GKzKFe-0-29dafb2a40e76aea344a66470dda3914)
这里仅展示了标签模块的接口注解编写,接下来应当参考上述接口注解,按照注解的含义完成文章模块接口注解的编写。
2.main方法
既然接口方法本身有了注解,那么针对这个项目,能不能写注解呢?如果有很多个项目,如何知道这个项目具体是哪个呢?实际上是可以识别出来的,只需针对main方法写入如下注解即可:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_88_1.jpg?sign=1740111459-RtGXcpyINTNfiY1SKhuPPVHD9p24vQ02-0-cf2e5b20c56d45edcfcb07bedd148f0d)
2.4.5 生成
在编写完所有的注解后,我们回到项目根目录下,执行如下命令:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_88_2.jpg?sign=1740111459-N2q9ZFmpUJ74tN5QDOvW3rQuuDMVwq8k-0-3538a357f481fec08d3b0ffbbd4ba4d1)
在执行命令后,可以发现在docs文件夹中生成了docs.go、swagger.json和swagger.yaml三个文件。
2.4.6 路由
至此,我们编写完了注解,也通过swag init把Swagger API所需要的文件都生成了,那么接下来该如何访问接口文档呢?其实很简单,只需在routers中进行默认初始化和注册对应的路由即可。打开internal/routers目录中的router.go文件,新增如下代码:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_88_3.jpg?sign=1740111459-1LAosppXOK34HYjgB7Q5KgXp0cWaLvni-0-db94c5f22e54107e9427f43fdcbb5d31)
在上述代码中,主要做了两件事,分别是初始化docs包和注册一个针对Swagger的路由。
在初始化 docs 包后,其 swagger.json 会默认指向当前应用所启动的域名下的swagger/doc.json路径,如果有额外需求,可手动指定,代码如下:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_88_4.jpg?sign=1740111459-CoDF0DI32rFl8h4qGROSy6Xz8MKZZ9om-0-6a58aee55963ae094754077e05cbb24a)
2.4.7 查看接口文档
在完成上述设置后,重新启动服务端,在浏览器中访问 Swagger 的地址http://127.0.0.1:8000/swagger/index.html,即可看到如图2-7所示的Swagger文档展示,其主要分为三部分,分别是项目主体信息、接口路由信息和模型信息。这三部分共同组成了主体内容。
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_89_1.jpg?sign=1740111459-u81D9Dbe3ARYNdyPTuV0ZWJCHSbyMoIm-0-206c0cbdfca4047d5f726cb68156cffb)
图2-7
2.4.8 源码分析
明明只是初始化了一个docs包并注册了一个Swagger相关路由,那么Swagger的文档是如何关联上的,在接口上写的注解又到哪里去了呢?
其实,我们的主体内容与2.4.4节生成的文件有关,分别是:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_89_2.jpg?sign=1740111459-2hFtJVN5HekSYdVGD90NNO56gZvLPmDv-0-6d28cdcef1eec86e754e97e02937112c)
1.初始化docs
在第一步中,我们初始化了docs包,对应的其实就是docs.go文件,因为目录下仅有一个go源文件,所以其源码如下:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_89_3.jpg?sign=1740111459-ckjXi8ig16JHdzB4GxxmIotX5bl8kI1M-0-90426627a47326b27e052c4ffdbec996)
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_90_1.jpg?sign=1740111459-5iZ4m1WalSy14Kiw5YSS17h25elMvFtB-0-1345e89adab04e0835678226148833e7)
通过对源码的分析可以得知,实际上在初始化docs包时,会默认执行init方法。而在init方法中,会注册相关方法,其主体逻辑是swag会在生成时检索项目下的注解信息,然后将项目信息和接口路由信息按规范生成到包全局变量doc中去。
接着在ReadDoc方法中做一些template的模板映射等工作,完善doc的输出。
2.注册路由
在上一步中,我们知道了生成的注解数据源的位置,它们两者又是如何关联起来的呢?实际上与调用的ginSwagger.WrapHandler(swaggerFiles.Handler)有关,代码如下:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_90_2.jpg?sign=1740111459-JlY0z2z47td3Vj8LDgEiY8QDvlqvQWfD-0-9dbb2f1b4214b58da653333b1606b608)
在调用WrapHandler后,swag内部会将其默认调用的URL设置为doc.json。doc.json又是从哪里来的呢?继续往下看,代码如下:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_91_1.jpg?sign=1740111459-FARR4ohjSZOBrn4jUqAgjYOsZi0Hj7X5-0-17c85e5e29f77edaf792a02747d1f866)
在CustomWrapHandler方法中,可以发现一个比较经典的switch…case逻辑。
在第一个 case 中,处理是的 index.html,这又是为什么呢?现在简单回顾一下,先前我们是通过http://127.0.0.1:8000/swagger/index.html访问到Swagger文档的,对应的便是这里的逻辑。
在第二个case中,可以大致解释我们关注的doc.json到底是什么。它相当于一个内部标识,可以读取生成的Swagger注解。先前在访问Swagger文档时,它顶部的文本框中的Explore默认的就是doc.json(也可以填写外部地址,只要输出的是对应的Swagger注解即可)。
2.4.9 存在的问题
细心的读者可能会发现,前面在介绍公共组件时已经定义好了一些基本类型的Response返回值,因而在本节编写成功响应时,只需直接调用model作为其数据类型即可,代码如下:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_91_2.jpg?sign=1740111459-zKKBKw7BQa6WhL5LjrtIUmRuR05jYtOJ-0-120298cea25625675f3d920e66eede37)
如果这样写,就会有一个问题,即当有model.Tag以外的字段时,如分页,就无法展示了。在实际编写代码中,我们常常会遇到某个对象内的某个字段是 interface,并且这个字段的类型是不定的,即公共结构体,那么注解又该如何写呢,如下面这种情况:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_91_3.jpg?sign=1740111459-Ipy5Gm9WjdP1HThbAZJxVTBuwG0r8Ya8-0-6bb955c41c94e88693689aac1a758975)
有的人会可能忽略它,采取口头说明,但这显然是不够的。而目前swag v1.6.5中也没有特别好的注解方式,官方在issue里曾表示过,通过注解来解决这个问题是不合理的,那么我们要怎么做呢?
实际上,官方给出的建议很简单,就是定义一个针对 Swagger 的对象,专门用于 Swagger接口文档的展示。首先,在internal/model的tag.go和article.go文件中,新增如下代码:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_92_1.jpg?sign=1740111459-55Ft2MKqOztfgBcl4Uy4WgQm8wjCWpdM-0-16f96035d414035b3106303f72acf526)
然后,修改接口方法中对应的注解信息:
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_92_2.jpg?sign=1740111459-qiEKcu27birNq0a63K5Qn9DU23YARlOP-0-44a8f5dce573bf1b1add4e786ab64716)
最后,只需在项目根目录下再次执行swag init,并在生成成功后重新启动服务端,就可以看到最新的效果了,如图2-8所示。
![](https://epubservercos.yuewen.com/4AF54D/17518673407513106/epubprivate/OEBPS/Images/39074_92_3.jpg?sign=1740111459-yxqTZo1WspmH2whfjIetoHJVVEraLgiO-0-9d5e153143b3c78f33fa7217c142297f)
图2-8
2.4.10 小结
本节我们简单介绍了Swagger和Swagger的相关生态圈组件,对所编写的API原型新增了响应的Swagger注解。接下来安装了针对Go语言的Swagger工具,用于后续的Swagger文档的生成和使用。