在我们对接企业微信的过程中,我们发现企业微信的 API 非常多,且参数各异。为了最大程度的减少重复工作,减少编码中的低级错误,我们决定借助工具来自动生成企业微信的 API 调用代码。
这个时候就看到了 Swagger
这个项目,一番尝试之后,竟然得到了如此方便好用的一套工具……
Github: github.com/juzibot/wecom-openapi
Demo: wecom-openapi.juzibot.com
OpenAPI 规范
OpenAPI 规范(OAS)定义了一个标准的、语言无关的 RESTful API 接口规范,它可以同时允许开发人员和计算机查看并理解某个服务的功能,而无需访问源代码、文档或网络流量检查,既方便人类学习和阅读,也方便机器阅读。正确定义 OAS 后,开发者可以使用最少的实现逻辑来理解远程服务并与之交互。
此外,文档生成工具可以使用 OpenAPI 规范来生成 API 文档,代码生成工具可以生成各种编程语言下的服务端和客户端代码,测试代码和其他用例。详见:OpenAPI Specification
WeCom-OpenAPI
企业微信并没有提供符合 OpenAPI 规范的文档,所以 WeCom-OpenAPI 项目应运而生。
WeCom-OpenAPI 是利用 NestJS 的 Swagger 集成能力来实现的,@nestjs/swagger 提供了一些里的装饰器(Decorators)来声明 Swagger 属性,方便管理 API 之间的关联关系。
使用方式
把代码克隆下来,安装依赖后启动项目。启动后会在根目录生成 swagger spec 文件 openapi.yaml,同时可以用浏览器打开 http://localhost:3000/openapi 查看 Swagger UI
$ git clone git@github.com:juzibot/wecom-openapi.git
$ cd wecom-opwenapi && npm install
$ npm run start
WeCom-OpenAPI 提供了对企业微信 API 的代理转发,所以你可以在 Swagger UI 上直接对 API 进行调试。首先将你的 AccessToken 配置好。
然后选择本地代理地址即可,这样就不会出现跨域的问题。
代码生成
主流的代码生成工具是 swagger-codegen,但是它生成的 golang 代码不太友好,所以这里推荐使用 go-swagger 项目,但是目前只支持 swagger 2.0 版本,所以需要将上面得到的 openapi.yaml 文件转换成一下版本。这里使用 api-spec-converter 来做版本转换。
$ npm install -g api-spec-converter
# 转换文档版本
$ api-spec-converter --from=openapi_3 --to=swagger_2 \
--syntax=yaml \
--order=alpha \
./openapi.yaml > swagger.yaml
# 安装 go-swaager
$ brew install go-swagger
$ mkdir wecom-api
# 生成 golang 代码,要求目标目录必须在 GO_PATH 中
$ cd wecom-api && go mod init wecom-api
# 生成 golang 代码
$ swagger generate client -f swagger.yaml -t wecom-api
至此,在 wecom-api 目录中会得到生成的代码文件
./wecom-api/
├── client
└── models
- client 目录中存放 Client 对象和 Parameter、Response 包装对象
- models 目录存放 Dto、Response 的实体定义
Example
package main
import (
"log"
"wecom-api/client"
"wecom-api/client/department"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
)
// 定义鉴权对象
type auth struct{}
var token = "token"
// 实现 ClientAuthInfoWriter 接口
func (a *auth) AuthenticateRequest(req runtime.ClientRequest, reg strfmt.Registry) error {
// 将 token 放在正确的位置
return req.SetQueryParam("access_token", token)
}
func main() {
departmentID := float64(1)
params := department.NewListDepartmentParams()
params.SetID(&departmentID)
resp, err := client.Default.Department.ListDepartment(params, &auth{})
if err != nil {
log.Fatalf("err: %v", err)
}
log.Printf("%v, %s", resp.Payload.Errcode, resp.Payload.Errmsg)
for _, v := range resp.Payload.Department {
log.Printf("%v", v)
}
}