写点什么

如何快速打造一款钉钉 Go sdk

用户头像
Ceelog
关注
发布于: 2020 年 12 月 16 日

事情并不简单



开发需要用到钉钉开放平台提供的接口,但是钉钉官方并没有提供 Go 语言版本的 sdk



在 GitHub 逛了一圈后,找不到满意的开源版本,有些 repo 设计不太规范,有些 repo 只实现了部分功能



于是,我决定亲手撸一个



但很快,我就发现事情并不简单:钉钉开放平台提供了超过 200 个接口!



开发一个完整的 sdk 需要阅读每一个接口的文档,理解各个参数的作用,处理各种异常和接口返回的结果,还有 access_token 管理、数据加解密等细节



即使是资深开发工程师,在这脏活累活上,恐怕也得耗上半个月的时间



太慢了,这不是我想要的



快就一个字



经过 30 分钟仔细阅读接口文档后,我发现了一些规律:



  • 服务器地址都是 https://oapi.dingtalk.com/

  • HTTP 请求方法都是 POST 或 GET

  • 所有的请求都需要 access_token 参数

  • 官方文档针对每一个接口都详细列出了接口简介、接口地址、Query 参数、Body 参数

  • ...



一个不太成熟的小想法就浮现出脑海:



  • 搞个爬虫抓取接口名称、接口简介、接口地址、Query 参数、Body 参数 等 API 元信息

  • 一个 API 的规格由其元信息唯一定义

  • 每个 API 都由一个 func 表示

  • params url.Values表示接口需要的Query 参数

  • payload []byte表示接口需要的Body 参数

  • resp []byte表示接口的返回内容

  • 根据 API 分组情况,拆分 package

  • 所有 API 请求由统一的 Http Client 模块处理

  • access_token、错误处理和重试逻辑由 Http Client 模块处理

  • 钉钉服务器发出回调由 Http Server 模块处理

  • ...



至此,一个简洁但完整的架构呼之欲出:



我们将打造这样一款 sdk : 介于业务逻辑 和 钉钉 之间,将业务的指令发送到钉钉,将钉钉的响应透传给业务



魔鬼在细节



按照上 part 我们的构想,就可以开始搞了



先看看钉钉文档页面结构:



启动爬虫抓回来接口元信息:

Api{
Name: "获取用户userid",
Description: "通过免登授权码和access_token获取用户的userid。",
Request: "GET https://oapi.dingtalk.com/user/getuserinfo?access_token=access_token&code=code",
See: "https://ding-doc.dingtalk.com/doc#/serverapi2/clotub",
FuncName: "GetUserInfo",
GetParams: []Param{
{Name: `code`, Type: `string`},
},
}



格式化成 Go func

package auth
import ...
const apiGetUserInfo = "/user/getuserinfo"
/*
获取用户userid
通过免登授权码和access_token获取用户的userid。
See: https://ding-doc.dingtalk.com/doc#/serverapi2/clotub
GET https://oapi.dingtalk.com/user/getuserinfo?access_token=access_token&code=code
*/
func GetUserInfo(ctx *dingding.App, params url.Values) (resp []byte, err error) {
return ctx.Client.HTTPGet(apiGetUserInfo + "?" + params.Encode())
}



再搞个 testexample 的模板代码,当爬虫全部执行完后,我们就可以得到:

├─ai
│ ai.go
│ ai_test.go
│ example_ai_test.go
├─alitrip
│ alitrip.go
│ alitrip_test.go
│ example_alitrip_test.go
├─attendance
│ attendance.go
│ attendance_test.go
│ example_attendance_test.go
......



Wow Amazing! 200 个接口已尽入吾彀中矣



不过,我们时间不多了:



  • 部分接口文档页面不规范,导致爬虫中断了 20 分钟

  • GET|POST 不同类型的请求测试函数的差异比较大,耗费了 40 分钟

  • ...



access_token 管理



所有接口调用最终都由统一的 Http Client 模块发起



由于每个接口调用都需要鉴权,所以我们把鉴权模块放在这里再好不过了



处理方式非常简单,拿到access_token,加到请求地址后即可:

accessToken, _ := client.Ctx.AccessToken.GetAccessTokenHandler()
newUrl := url + "?access_token=" + accessToken



关键在 GetAccessTokenHandler方法,这里定义了如何获取 access_token



试一试



再处理一下其他细节,包括错误处理、单元测试等,我们的 sdk 就差不多搞定啦



现在到了 Eating your own dog food环节:

go get github.com/fastwego/dingding



// 创建应用实例
app := dingding.NewApp(dingding.AppConfig{
AppKey: viper.GetString("AppKey"),
AppSecret: viper.GetString("AppSecret"),
})
// 调用 api
params := url.Values{}
params.Add("code", CODE)
resp, err := auth.GetUserInfo(app, params)

如果一切正常的话,接口可以正确响应我们的请求



效果好极啦,调用简单,不仅有 Example 引导,还有文档和链接:





果然还是自己打造的工具最趁手



才刚刚开始



在同样的思路下,我又撸完了企业微信和飞书的 Go sdk



  • https://github.com/fastwego/wxwork

  • https://github.com/fastwego/feishu



to be continue ...



发布于: 2020 年 12 月 16 日阅读数: 724
用户头像

Ceelog

关注

微信公众号 Ceelog 2017.10.18 加入

Faster we go together

评论 (1 条评论)

发布
用户头像
把文档转成openapi,然后要什么语言实现都有了。
2020 年 12 月 17 日 14:53
回复
没有更多了
如何快速打造一款钉钉 Go sdk