对接腾讯云未集成到SDK的接口开发实践小记
# 前言
日常工作中,与腾讯云打交道,为了提高效率,会调用它的接口来完成一些重复性工作。一般都是通过官方集成好的 SDK 来完成交互。
但有时候也存在一种情况,那就是这个接口还没有集成到 SDK 当中,又需要进行调用,此时可通过原生方式生成 curl
请求,以达到调用目的。
本文记录如何快速实现并完成这样一个流程,以此记录,便于后续再次查阅。
# 认识原生请求方法
通常在每个产品的 API 接口文档页面,都会在调用方式里边有一个 签名方法 v3
的页面,在这个页面,介绍了如何自助生成签名,完成接口的调用。
这里以云服务器的签名方法 v3 (opens new window) 的文档介绍为例,来简单认识一下这一思路的用法。
我这里选择其中提供的 Golang 代码示例来做演示:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"strings"
"time"
)
func sha256hex(s string) string {
b := sha256.Sum256([]byte(s))
return hex.EncodeToString(b[:])
}
func hmacsha256(s, key string) string {
hashed := hmac.New(sha256.New, []byte(key))
hashed.Write([]byte(s))
return string(hashed.Sum(nil))
}
func main() {
// 需要设置环境变量 TENCENTCLOUD_SECRET_ID,值为示例的 AKIDz8krbsJ5yKBZQpn74WFkmLPx3*******
secretId := os.Getenv("TENCENTCLOUD_SECRET_ID")
// 需要设置环境变量 TENCENTCLOUD_SECRET_KEY,值为示例的 Gu5t9xGARNpq86cd98joQYCN3*******
secretKey := os.Getenv("TENCENTCLOUD_SECRET_KEY")
host := "cvm.tencentcloudapi.com"
algorithm := "TC3-HMAC-SHA256"
service := "cvm"
version := "2017-03-12"
action := "DescribeInstances"
region := "ap-guangzhou"
//var timestamp int64 = time.Now().Unix()
var timestamp int64 = 1551113065
// step 1: build canonical request string
httpRequestMethod := "POST"
canonicalURI := "/"
canonicalQueryString := ""
canonicalHeaders := fmt.Sprintf("content-type:%s\nhost:%s\nx-tc-action:%s\n",
"application/json; charset=utf-8", host, strings.ToLower(action))
signedHeaders := "content-type;host;x-tc-action"
payload := `{"Limit": 1, "Filters": [{"Values": ["\u672a\u547d\u540d"], "Name": "instance-name"}]}`
hashedRequestPayload := sha256hex(payload)
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
httpRequestMethod,
canonicalURI,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
hashedRequestPayload)
fmt.Println(canonicalRequest)
// step 2: build string to sign
date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")
credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, service)
hashedCanonicalRequest := sha256hex(canonicalRequest)
string2sign := fmt.Sprintf("%s\n%d\n%s\n%s",
algorithm,
timestamp,
credentialScope,
hashedCanonicalRequest)
fmt.Println(string2sign)
// step 3: sign string
secretDate := hmacsha256(date, "TC3"+secretKey)
secretService := hmacsha256(service, secretDate)
secretSigning := hmacsha256("tc3_request", secretService)
signature := hex.EncodeToString([]byte(hmacsha256(string2sign, secretSigning)))
fmt.Println(signature)
// step 4: build authorization
authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
algorithm,
secretId,
credentialScope,
signedHeaders,
signature)
fmt.Println(authorization)
curl := fmt.Sprintf(`curl -X POST https://%s\
-H "Authorization: %s"\
-H "Content-Type: application/json; charset=utf-8"\
-H "Host: %s" -H "X-TC-Action: %s"\
-H "X-TC-Timestamp: %d"\
-H "X-TC-Version: %s"\
-H "X-TC-Region: %s"\
-d '%s'`, host, authorization, host, action, timestamp, version, region, payload)
fmt.Println(curl)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
如上命令运行之后,最后会打印该 curl 请求如下:
$ curl -X POST https://cvm.tencentcloudapi.com\
-H "Authorization: TC3-HMAC-SHA256 Credential=***EG1Yac266rMcU6uZO****AhzRdZaRqMRG/2019-02-25/cvm/tc3_request, SignedHeaders=content-type;host;x-tc-action, Signature=b**cc7cd1c906c828ba52266b543816adf61a6df9af2e4da708fb790"\
-H "Content-Type: application/json; charset=utf-8"\
-H "Host: cvm.tencentcloudapi.com" -H "X-TC-Action: DescribeInstances"\
-H "X-TC-Timestamp: 1551113065"\
-H "X-TC-Version: 2017-03-12"\
-H "X-TC-Region: ap-guangzhou"\
-d '{"Limit": 1, "Filters": [{"Values": ["\u672a\u547d\u540d"], "Name": "instance-name"}]}'
2
3
4
5
6
7
8
整个代码里边每个步骤的操作也都有注释,就不一一解释了。这里简单概述一下,如上代码,除去生成签名的步骤之外,还有五个部分,来完成此命令的渲染:
host:
指定要请求的域名地址。service:
指定服务类型version:
:指定接口版本action:
:指定请求的动作,即对应的接口region:
:指定资源的区域payload:
:指定请求的参数
# 集成官方提供的方法
如上方法对应的代码,一大块儿都是签名的处理,猛地一看这些代码,可能也很懵,我一开始还想着如何简化抽象一下签名的方法,就在简化的过程中,一检索,发现,事实上官方的 SDK 也提供了简化的方案。
比如 golang 的 SDK,看官方 readme,有专门的介绍:结合 common client 自定义请求 (opens new window)。
其中也提供了代码示例:
package main
import (
"fmt"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
"log"
"os"
)
// 目前只支持 签名v3+POST
func main() {
credential := common.NewCredential(
os.Getenv("TENCENTCLOUD_SECRET_ID"),
os.Getenv("TENCENTCLOUD_SECRET_KEY"))
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "cvm.tencentcloudapi.com"
cpf.HttpProfile.ReqMethod = "POST"
//创建common client
client := common.NewCommonClient(credential, regions.Guangzhou, cpf).WithLogger(log.Default())
// 创建common request,依次传入产品名、产品版本、接口名称
request := tchttp.NewCommonRequest("cvm", "2017-03-12", "DescribeZones")
// 自定义请求参数:
// SetActionParameters 函数支持三种数据类型的入参:
// 1. map[string]interface{}
// body:=map[string]interface{}{
// "InstanceId":"crs-xxx",
// "SpanType":2,
// }
body := map[string]interface{}{}
// // 2. string
// bodyStr := `{}`
// // 3. []byte
// bodyBytes := []byte(bodyStr)
// set custom headers
request.SetHeader(map[string]string{
"X-TC-TraceId": "ffe0c072-8a5d-4e17-8887-a8a60252abca",
})
// 设置action所需的请求数据
err := request.SetActionParameters(body)
if err != nil {
return
}
//创建common response
response := tchttp.NewCommonResponse()
//发送请求
err = client.Send(request, response)
if err != nil {
fmt.Printf("fail to invoke api: %v \n", err)
}
// 获取响应结果
fmt.Println(string(response.GetBody()))
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
借助于官方封装好的 client,就可以直接拼装接口,完成调用了。
这里贴一个自己封装的,将 MySQL 实例设置为只读的方法,以供参考:
// SetCdbReadOnly 设置cdb实例只读 0:只读 1:读写
func SetCdbReadOnly(credential *common.Credential, instanceid string, readonly int) (*tchttp.CommonResponse, error) {
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "cdb.tencentcloudapi.com"
client := common.NewCommonClient(credential, regions.Shanghai, cpf).WithLogger(log.Default())
request := tchttp.NewCommonRequest("cdb", "2017-03-20", "ModifyDBInstanceReadOnlyStatus")
body := map[string]interface{}{
"InstanceId": instanceid,
"ReadOnly": readonly,
}
err := request.SetActionParameters(body)
if err != nil {
return nil, err
}
response := tchttp.NewCommonResponse()
err = client.Send(request, response)
if err != nil {
return nil, err
}
return response, nil
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
如此一来,要想自定义封装一些尚未集成到 SDK 的方法,就变得比较简单了。
# 如何快速找到接口
很多时候,一些相对偏门的接口,可能第一步困难的是如何找到这个接口相关的文档说明,这里也记录一下我个人的一些心得。
方法一:
通过自己的渠道,直接找到腾讯云官方的人员,咨询对应的接口,这是最高效也是最可靠的方式。
方法二:
- 通过腾讯云 API 平台检索,点我跳转。 (opens new window)
- 或者通过 API Inspector 找到对应接口,点我看相关介绍 (opens new window)。
- 如果上边两种方案都不行,那就只能 F12 通过点击页面,生看生撸接口了。