Skip to main content

Bugly OpenAPI 接入使用说明

1. 说明

专业版域名https://api.bugly.tds.qq.com

海外版域名https://api-buglysgp.tds.tencent.com

1.1 接入流程

  1. 接入申请:接入方需要申请对应的接口方可使用,请与 Bugly 助手联系(提供产品 appid、appkey)
  2. 阅读本文档、参考示例代码

2. 版本发布

2.1 /v1/version/set_versions_release 设置版本发布

请求说明

参数名参数类型参数说明是否必填
product_idstring产品IDY
versions.release_datenumber发布时间/灰度时间Y
versions.versionstringapp版本Y
versions.version_typenumber版本类型(1-开发版本,2-灰度版本,3-发布版本)Y

响应说明

参数名字参数类型参数说明
baseRsp.codeint错误码,默认0
baseRsp.msgstring错误信息

示例

curl -X POST 'https://api.bugly.tds.qq.com/v1/version/set_versions_release' \
-H 'Authorization: apiID={apiID}&hashedPayload={计算的hashedPayload}&nonce={nonce}&signMethod=HmacSHA256&timestamp={timestamp}&version=202100&signature={计算的signature}' \
-H 'Content-Type: application/json' \
-H 'X-ProductId: {ProductId}' \
-H 'X-ProductKey: {ProductKey}' \
-d '{"product_id":"a278f01047","versions":[{"release_date":1760424983,"version":"1.2.3","version_type":1}]}'

3. 附录

Header 公共参数

字段必填描述
X-Gateway-RequestID请求ID,用于定位问题,没有格式和内容的要求
• 如果客户端请求带有X-Gateway-RequestID字段,则使用客户端传来的id回复
• 如果客户端请求未指定X-Gateway-RequestID字段,则网关生成唯一的X-Gateway-RequestID回复
X-ProductId产品注册的ID(即Bugly的APPID)产品页面的"设置/产品信息"可以查询到,后续优化可能不需要,先填着
X-ProductKey产品注册到Bugly时,Bugly平台自动分配,用于鉴权(即Bugly的AppKey)产品页面的"设置/产品信息"可以查询到,后续优化可能不需要,先填着
Authorization参考下面签名计算方法

3.1 签名计算方法

3.1.1 必要参数

参数名称类型是否必填示例备注
apiIDstring必填Bugly分配给调用方的id,一般是一个公司一个idapiID,用于身份识别,这个信息申请接口时可以拿到
apiKeystring必填-apiKey,用于接口身份校验,需要保存在配置里不暴露给外部
hashedPayloadstring有body字段必填UodgxU3P77iThrEJtsiHi2kjYJmNA2jGEgYNnMD%2FX0s%3D包体签名
nonceint必填随机正整数,最小值为100000用于防重放攻击
signaturestring必填enonndonfewiodgxUEJtsiHi2kjYJmNA2jGEgYNnMD%2fX0s%3D请求签名
signMethodstring必填固定值:HmacSHA256签名方式
timestampint必填当前时间戳(秒):1569490800请求有效期为60秒
versionstring必填固定值:202100用于后续签名升级

3.1.2 签名的流程

1)包体签名的生成过程(hashedPayload)

(1)用 HmacSHA256 签名,生成16进制的包体签名串,示例如下:需要传入 request body 字段和 appKey 进行加密

sha := HmacSha256(body string, appKey string)

(2)签名串编码:Base64 编码

base64Str := base64.StdEncoding.EncodeToString([]byte(sha))

(3)url 编码后得到加密串

hashedPayload := url.QueryEscape(base64Str)

2)签名串的生成过程

(1)拼接需要签名的字符串

signStr := "apiID=f39d4525ad&hashedPayload=UodgxU3P77iThrEJtsiHi2kjYJmNA2jGEgYNnMD%2FX0s%3D&nonce=202100&signMethod=HmacSHA256&timestamp=1569490800&version=202100"

(2)用 HmacSHA256 签名,生成16进制的包体签名串,示例如下:需要传入 request body 字段和 appKey 进行加密

sha := HmacSha256(signStr string, appKey string)

(3)签名串编码:Base64 编码

base64Str := base64.StdEncoding.EncodeToString([]byte(sha))

(4)url 编码后得到加密串

signature := url.QueryEscape(base64Str)

(5)拼接最终的签名串

  1. 拼接的签名串,必须是第(1)步拼接的签名串和请求签名进行拼接
  2. 需规定签名信息 signature 必须作为最后一个参数,拼接在最后面,以便截取,进行签名验证
  3. 所有请求参数的参数值均需要做 URL 编码,需要注意的是,部分语言库会自动对 URL 进行编码,重复编码会导致签名校验失败
authorization := signStr + "&signature=enonndonfewiodgxUEJtsiHi2kjYJmNA2jGEgYNnMD%2fX0s%3D"

最终生成的签名串示例:

apiID=198732198&hashedPayload=UodgxU3P77iThrEJtsiHi2kjYJmNA2jGEgYNnMD%2FX0s%3D&nonce=202100&signMethod=HmacSHA256&timestamp=1569490800&version=202100&signature=enonndonfewiodgxUEJtsiHi2kjYJmNA2jGEgYNnMD%2fX0s%3D

3)将签名信息添加到 http 请求 header 的 "Authorization" 字段中


3.2 示例代码

package entity

import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)

const signMethod = "HmacSHA256" // 签名的方法
const version = "202100" // 版本号

// computeHMacSHA256 获取加密数据
func computeHMacSHA256(message string, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(message))
// hamcSha256加密成16进制的字符串
sha := hex.EncodeToString(h.Sum(nil))
// fmt.Printf("sha: %s\n", sha)
// base64编码
b64 := base64.StdEncoding.EncodeToString([]byte(sha))
// fmt.Printf("base64Str: %s\n", base64Str)
// url编码
return url.QueryEscape(b64)
}

// generateAuthorization 获取签名认证
func generateAuthorization(apiID, apiKey, body string) string {
var signString string
nonce := time.Now().UnixNano() // 生成一个大于六位的随机数
timestamp := time.Now().Unix() // 生成时间戳
if len(body) > 0 {
hashedPayload := computeHMacSHA256(body, apiKey)
signString = fmt.Sprintf("apiID=%s&hashedPayload=%s&nonce=%d&signMethod=%s&timestamp=%d&version=%s",
apiID, hashedPayload, nonce, signMethod, timestamp, version)
} else {
signString = fmt.Sprintf("apiID=%s&nonce=%d&signMethod=%s&timestamp=%d&version=%s",
apiID, nonce, signMethod, timestamp, version)
}
signature := computeHMacSHA256(signString, apiKey)
return signString + "&signature=" + signature
}

// Request 发送http post请求
func Request(url string, body *bytes.Buffer) ([]byte, error) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := http.NewRequest("POST", url, body) // 发送的body必须和生成签名的body数据一致,不然校验签名会不通过
if err != nil {
return nil, err
}
authorization := generateAuthorization(APIID, APIKey, body.String())
req.Header.Add("Authorization", authorization)
req.Header.Add("content-type", "application/json")
req.Header.Add("X-ProductId", ProductID)
req.Header.Add("X-ProductKey", ProductKey)
req.Header.Add("HOST", "bugly.woa.com")
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
} else if res.StatusCode != 200 {
return nil, errors.New("response error: " + string(resBody))
}
return resBody, nil
}