You need to enable JavaScript to run this app.
导航

使用 HTTP 请求调用

最近更新时间2023.08.16 15:09:27

首次发布时间2023.02.08 13:10:40

您通过 HTTP 请求来调用火山引擎私网解析(PrivateZone)的 API。

在发送 HTTP 请求前,您需要理解以下内容:

请求结构

API 请求的结构包含以下内容:

  • API 服务地址。
  • 通讯协议。
  • 请求方法。
  • 请求参数。

API 服务地址

私网解析 PrivateZone 的服务地址是 open.volcengineapi.com

通讯协议

您可以使用 HTTP 协议或 HTTPS 协议发送请求。推荐您使用 HTTPS 协议,其安全性更高。

请求方法

关于 API 所使用的方法,参见每个 API 的说明。对于 POST 请求,您必须在请求头中指定 Content-Type: application/json

请求参数

请求参数包括公共参数和每个 API 所特有的参数。

公共参数

公共参数是每个 API 请求必须包含的参数。如果一个 API 请求缺失公共参数,请求会失败。以下表格中的公共参数必须包含在查询字符串(query string)中。

参数名称数据类型是否必选说明示例
Actionstring表示 API 名称。格式为 [a-zA-Z]+CreatePrivateZone
Versionstring表示 API 版本。该参数的取值是 2022-06-012022-06-01
X-ExpiresInteger签名的有效时间,单位为秒。默认值为 900。900

请求鉴权的示例代码

每个请求中必须包含鉴权信息。该鉴权信息用以验证请求者的身份。

Golang 示例代码

下面的示例代码演示了如何基于 Golang 在调用 UpdatePrivateZone APIListPrivateZones API 时对请求进行鉴权。

在运行示例代码之前,您需要 获取 Access Key ID 和 Secret Access Key。然后,您需要分别将 Access Key ID 和 Secret Access Key 的值传入示例代码中的 AccessKey 常量和 SecretAccessKey 常量。

package main

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

const Version = "2022-06-01"
const Service = "private_zone"
const Region = "cn-north-1"
const Host = "open.volcengineapi.com"

// 第一步:准备辅助函数。
// sha256非对称加密
func hmacSHA256(key []byte, content string) []byte {
   mac := hmac.New(sha256.New, key)
   mac.Write([]byte(content))
   return mac.Sum(nil)
}

// sha256 hash算法
func hashSHA256(content []byte) string {
   h := sha256.New()
   h.Write(content)
   return hex.EncodeToString(h.Sum(nil))
}

// 第二步:准备需要用到的结构体定义。
// 签算请求结构体
type RequestParam struct {
   Body      []byte
   Method    string
   Date      time.Time
   Path      string
   Host      string
   QueryList url.Values
}

// 身份证明结构体
type Credentials struct {
   AccessKeyID     string
   SecretAccessKey string
   Service         string
   Region          string
}

// 签算结果结构体
type SignRequest struct {
   XDate          string
   Host           string
   ContentType    string
   XContentSha256 string
   Authorization  string
}

// go http client
var httpClient = &http.Client{
   Timeout: time.Second * 60,
   Transport: &http.Transport{
      MaxIdleConns:    100,
      MaxConnsPerHost: 10,
      IdleConnTimeout: time.Second * 15,
   },
}

// 第三步:创建一个私网域名解析的 API 请求函数。签名计算的过程包含在该函数中。
func requestPrivateZone(method string, query map[string][]string, header map[string]string, ak string, sk string, action string, body []byte) ([]byte, error) {
   // 第四步:在 requestPrivateZone 中,创建一个 HTTP 请求实例。
   // 创建 HTTP 请求实例。该实例会在后续用到。
   request, _ := http.NewRequest(method, "https://"+Host+"/", bytes.NewReader(body))
   urlVales := url.Values{}
   for k, v := range query {
      urlVales[k] = v
   }
   urlVales["Action"] = []string{action}
   urlVales["Version"] = []string{Version}
   request.URL.RawQuery = urlVales.Encode()
   for k, v := range header {
      request.Header.Set(k, v)
   }
   // 第五步:创建身份证明。其中的 Service 和 Region 字段是固定的。ak 和 sk 分别代表 AccessKeyID 和 SecretAccessKey。同时需要初始化签名结构体。一些签名计算时需要的属性也在这里处理。
   // 初始化身份证明
   credential := Credentials{
      AccessKeyID:     ak,
      SecretAccessKey: sk,
      Service:         Service,
      Region:          Region,
   }
   // 初始化签名结构体
   requestParam := RequestParam{
      Body:      body,
      Host:      request.Host,
      Path:      "/",
      Method:    request.Method,
      Date:      time.Now().UTC(),
      QueryList: request.URL.Query(),
   }
   // 第六步:接下来开始计算签名。在计算签名前,先准备好用于接收签算结果的 signResult 变量,并设置一些参数。
   // 初始化签名结果的结构体
   xDate := requestParam.Date.Format("20060102T150405Z")
   shortXDate := xDate[:8]
   XContentSha256 := hashSHA256(requestParam.Body)
   contentType := "application/json"
   signResult := SignRequest{
      Host:           requestParam.Host, // 设置Host
      XContentSha256: XContentSha256,    // 加密body
      XDate:          xDate,             // 设置标准化时间
      ContentType:    contentType,       // 设置Content-Type 为 application/json
   }
   // 第七步:计算 Signature 签名。
   signedHeadersStr := strings.Join([]string{"content-type", "host", "x-content-sha256", "x-date"}, ";")
   canonicalRequestStr := strings.Join([]string{
      requestParam.Method,
      requestParam.Path,
      request.URL.RawQuery,
      strings.Join([]string{"content-type:" + contentType, "host:" + requestParam.Host, "x-content-sha256:" + XContentSha256, "x-date:" + xDate}, "\n"),
      "",
      signedHeadersStr,
      XContentSha256,
   }, "\n")
   hashedCanonicalRequest := hashSHA256([]byte(canonicalRequestStr))
   credentialScope := strings.Join([]string{shortXDate, credential.Region, credential.Service, "request"}, "/")
   stringToSign := strings.Join([]string{
      "HMAC-SHA256",
      xDate,
      credentialScope,
      hashedCanonicalRequest,
   }, "\n")
   kDate := hmacSHA256([]byte(credential.SecretAccessKey), shortXDate)
   kRegion := hmacSHA256(kDate, credential.Region)
   kService := hmacSHA256(kRegion, credential.Service)
   kSigning := hmacSHA256(kService, "request")
   signature := hex.EncodeToString(hmacSHA256(kSigning, stringToSign))
   signResult.Authorization = fmt.Sprintf("HMAC-SHA256 Credential=%s, SignedHeaders=%s, Signature=%s", credential.AccessKeyID+"/"+credentialScope, signedHeadersStr, signature)
   // 第八步:将 Signature 签名写入HTTP Header 中,并发送 HTTP 请求。
   // 设置经过签名的5个HTTP Header
   request.Header.Set("Host", signResult.Host)
   request.Header.Set("Content-Type", signResult.ContentType)
   request.Header.Set("X-Date", signResult.XDate)
   request.Header.Set("X-Content-Sha256", signResult.XContentSha256)
   request.Header.Set("Authorization", signResult.Authorization)
   resp, err := httpClient.Do(request)
   if err != nil {
      return nil, err
   }
   body, err = ioutil.ReadAll(resp.Body)
   if err != nil {
      return nil, err
   }
   return body, nil
}

/* main.go */

// 定义请求参数结构体
type UpdateZoneParam struct {
   ZID    uint64 `json:"ZID"`
   Remark string `json:"Remark"`
}

func main() {
    // (POST 请求)调用 UpdatePrivateZone API
    bodyParam := UpdateZoneParam{
       ZID:    100,
       Remark: "example",
    }
    body, err := json.Marshal(&bodyParam)
    if err != nil {
       fmt.Printf("marshal failed: %v", err)
       return
    }

    // 分别将 Access Key ID 和 Secret Access Key 的值传入 AK 常量和 SK 常量
    AccessKey := "ak"
    SecretAccessKey := "sk"

    updatePrivateZoneResult, err := requestDNS("POST", map[string][]string{}, map[string]string{}, AccessKey, SecretAccessKey, "UpdatePrivateZone", body)
   if err != nil {
      fmt.Printf("do request failed: %v", err)
      return
   }
   // 打印输出的结果
    fmt.Println(string(updatePrivateZoneResult))

    // (GET 请求)调用 ListPrivateZones API
    QueryParam := map[string][]string{"KeyWord": []string{"example.com"}}

    ListPrivateZonesResult, err := requestDNS("GET", QueryParam, map[string]string{}, AccessKey, SecretAccessKey,
       "ListPrivateZones", []byte{})
    if err != nil {
       fmt.Printf("do request failed: %v", err)
       return
    }
    // 打印输出的结果
    fmt.Println(string(ListPrivateZonesResult))

}

Python 3 示例代码

下面的示例代码演示了如何基于 Python 3 在调用 UpdatePrivateZone APIListPrivateZones API 时对请求进行鉴权。

在运行示例代码之前,您需要 获取 Access Key ID 和 Secret Access Key。然后,您需要分别将 Access Key ID 和 Secret Access Key 的值传入示例代码中的 AK 变量和 SK 变量。另外,示例代码使用了 requests 库,您可以通过 python3 -m pip install requests 命令安装 requests 库。

import datetime
import hashlib
import hmac
import json
from urllib.parse import quote
from collections import OrderedDict

import requests

Service = "private_zone"
Version = "2022-06-01"
Region = "cn-north-1"
Host = "open.volcengineapi.com"
# 分别将 Access Key ID 和 Secret Access Key 的值传入 AK 变量和 SK 变量
AK = "ak"
SK = "sk"


def norm_query(params):
    query = ""
    for key in sorted(params.keys()):
        if type(params[key]) == list:
            for k in params[key]:
                query = (
                        query + quote(key, safe="-_.~") + "=" + quote(k, safe="-_.~") + "&"
                )
        else:
            query = (query + quote(key, safe="-_.~") + "=" + quote(params[key], safe="-_.~") + "&")
    query = query[:-1]
    return query.replace("+", "%20")


# 第一步:准备辅助函数。
# sha256 非对称加密
def hmac_sha256(key: bytes, content: str):
    return hmac.new(key, content.encode("utf-8"), hashlib.sha256).digest()


# sha256 hash算法
def hash_sha256(content: str):
    return hashlib.sha256(content.encode("utf-8")).hexdigest()


# 第二步:创建一个 API 请求函数。签名计算的过程包含在该函数中。
def request(method, query, header, ak, sk, action, body):
    # 第三步:创建身份证明。其中的 Service 和 Region 字段是固定的。ak 和 sk 分别代表
    # AccessKeyID 和 SecretAccessKey。同时需要初始化签名结构体。一些签名计算时需要的属性也在这里处理。
    # 初始化身份证明结构体
    credential = {
        "access_key_id": ak,
        "secret_access_key": sk,
        "service": Service,
        "region": Region,
    }
    # 初始化签名结构体
    query = {"Action": action, "Version": Version, **query}
    sorted_query = OrderedDict(sorted(query.items()))

    request_param = {
        "body":"",
        "host": Host,
        "path": "/",
        "method": method,
        "content_type": "application/json",
        "date": datetime.datetime.utcnow(),
        "query": sorted_query,
    }

    if method == "POST":
        request_param["body"] = json.dumps(body)

    # 第四步:接下来开始计算签名。在计算签名前,先准备好用于接收签算结果的 signResult 变量,并设置一些参数。
    # 初始化签名结果的结构体
    x_date = request_param["date"].strftime("%Y%m%dT%H%M%SZ")
    short_x_date = x_date[:8]
    x_content_sha256 = hash_sha256(request_param["body"])
    sign_result = {
        "Host": request_param["host"],
        "X-Content-Sha256": x_content_sha256,
        "X-Date": x_date,
        "Content-Type": request_param["content_type"],
    }
    # 第五步:计算 Signature 签名。
    signed_headers_str = ";".join(
        ["content-type", "host", "x-content-sha256", "x-date"]
    )
    canonical_request_str = "\n".join(
        [request_param["method"],
         request_param["path"],
         norm_query(request_param["query"]),
         "\n".join(
             [
                 "content-type:" + request_param["content_type"],
                 "host:" + request_param["host"],
                 "x-content-sha256:" + x_content_sha256,
                 "x-date:" + x_date,
             ]
         ),
         "",
         signed_headers_str,
         x_content_sha256,
         ]
    )
    hashed_canonical_request = hash_sha256(canonical_request_str)
    credential_scope = "/".join([short_x_date, credential["region"], credential["service"], "request"])
    string_to_sign = "\n".join(["HMAC-SHA256", x_date, credential_scope, hashed_canonical_request])
    k_date = hmac_sha256(credential["secret_access_key"].encode("utf-8"), short_x_date)
    k_region = hmac_sha256(k_date, credential["region"])
    k_service = hmac_sha256(k_region, credential["service"])
    k_signing = hmac_sha256(k_service, "request")
    signature = hmac_sha256(k_signing, string_to_sign).hex()
    sign_result["Authorization"] = "HMAC-SHA256 Credential={}, SignedHeaders={}, Signature={}".format(
        credential["access_key_id"] + "/" + credential_scope,
        signed_headers_str,
        signature,
    )
    header = {**header, **sign_result}
    # 第六步:将 Signature 签名写入 HTTP Header 中,并发送 HTTP 请求。
    if method == "POST":
        r = requests.post("https://{}{}".format(request_param["host"], request_param["path"]),
                          headers=header,
                          params=request_param["query"],
                          data=request_param["body"],
                          )
        return r.json()
    if method == "GET":
        r = requests.get("https://{}{}".format(request_param["host"], request_param["path"]),
                          headers=header,
                          params=request_param["query"],
                          data=request_param["body"],
                          )
        return r.json()


if __name__ == "__main__":
    # (POST 请求)调用 UpdatePrivateZone API
    request_body = {
        "ZID": 100,
        "Remark": "example",
    }
    update_private_zone_result = request("POST", {}, {}, AK, SK, "UpdatePrivateZone", request_body)
    print(update_private_zone_result)

    # (GET 请求)调用 ListPrivateZones API
    request_query = {"KeyWord": "example.com"}
    list_private_zones_result = request("GET", request_query, {}, AK, SK, "ListPrivateZones", {})
    print(list_private_zones_result)

请求鉴权算法

您可以使用以下任一方法提供鉴权信息。推荐您使用方法一。

(推荐)方法一:在请求头中包含鉴权信息

您需要在请求头中包含以下参数:

参数名称数据类型是否必选参数说明示例
X-Datestring表示签名计算的时间,以 UTC 表示。时间精度是秒。
20201103T104027Z

Authorization

string

该参数表示鉴权字符串。Authorization 的伪代码结构如下:

HMAC-SHA256 Credential = {AccessKey}/{ShortDate}/{Region}/{Service}/{Request}, SignedHeaders={SignedHeaders}, Signature={Signature}

Authorization 伪代码中的 Signature 参数表示签名。关于 Signature 参数的计算步骤,参见签名计算方式。关于 Authorization 伪代码中其他参数的说明,参见伪代码中参数的说明

该参数表示一个结构体。结构体中包含了 Signature

X-Security-Tokenstring如果您使用火山引擎账号的 Access Key ID 和 Secret Access Key 来计算 Authorization,则无需指定该参数。如果您使用的 Access Key ID 和 Secret Access Key 是安全令牌服务(STS)提供的,则需要指定该参数。该参数值就是 STS 颁发的临时安全凭证中的 SessionToken。参见 获取临时安全令牌STSeyJBY2NvdW50SW

请求示例

GET https://open.volcengineapi.com/?Action=ListPrivateZones&Version=2022-06-01

X-Date: 20230116T073702Z
Authorization: HMAC-SHA256 Credential=AKLTMjYxYTZmYWU4ZWYzNGI2NDg8NTUxODE1ZGVhNmIxZmQ/20230116/cn-north-1/private_zone/request, SignedHeaders=x-content-sha256;x-date, Signature=5a394ce80456c7cdf989c28bd638807c8ead386eb15dd36e39952f405380aef2
ServiceName: private_zone

方法二:在查询字符串中包含鉴权信息

您需要在查询字符串中包含以下查询参数。

参数名称数据类型是否必选参数说明示例
X-Datestring签名计算的时间,以 UTC 表示。时间精度是秒。
20201103T104027Z
X-Algorithmstring签名计算所使用的算法。该参数的值是 HMAC-SHA256HMAC-SHA256

X-Credential

string

X-Credential 的伪代码结构如下:

{AccessKey}/{ShortDate}/{Region}/{Service}/{Request}

关于 X-Credential伪代码中参数的说明,参见伪代码中参数的说明

AKLTMjI2ODVlYzI3ZGY1NGU4ZjhjYWRjMTlmNTM5OTZkYzE/20210913/cn-north-1/private_zone/request

X-SignedHeaders

string

参与签名计算的请求头参数。多个请求头参数使用分号(;)分隔。这些请求头参数是根据参数名称升序排序的。X-SignedHeadersSignedHeaders 参数的定义是相同的。在您计算 CanonicalRequest 的参数值的时候需要使用 X-SignedHeaders

一般来说,X-SignedHeaders 的值是 host;x-content-sha256;x-date。您也可以指定任意请求头参数作为 X-SignedHeaders 的值。

host;x-content-sha256;x-date

X-Signaturestring一个经过计算得到的签名。关于签名的计算步骤,参见签名计算方式

签名计算方式

本章节主要介绍签名是如何计算的。

  • 本章节的伪代码中的 HMAC 方法使用的是 HMAC-SHA256 算法。
  • 本章节的伪代码中的 HexEncode 方法将字符串转换为十六进制编码格式的字符串。

Signature 是基于 kSigningStringToSign 参数计算而来的。Signature 的伪代码如下:

Signature = HexEncode(HMAC(kSigning, StringToSign))

kSigning

kSigning 表示用来计算签名的密钥。要计算 kSigning,您必须先获取您账号的 Access Key Secret,然后使用以下伪代码生成 kSigning

kSecret = <Your Access Key Secret>
kDate = HMAC(kSecret, ShortDate)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "request")

关于 kSigning 伪代码中参数的说明,参见伪代码中参数的说明

StringToSign

StringToSign 表示用来计算签名的签名字符串。StringToSign 的伪代码如下:

// Hash 函数使用 SHA256 算法
StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HexEncode(Hash(CanonicalRequest))

关于 StringToSign 伪代码中 AlgorithmRequestDate 参数的说明,参见伪代码中参数的说明。StringToSign 伪代码中其他参数的说明如下。

CredentialScope

CredentialScope 表示凭证范围。CredentialScope 的伪代码如下:

{ShortDate}/{Region}/{Service}/{Request}

关于 CredentialScope 伪代码中参数的说明,参见伪代码中参数的说明

CanonicalRequest

CanonicalRequest 表示规范的请求。CanonicalRequest 的伪代码如下:

HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Hash(RequestPayload))

CanonicalRequest 伪代码中参数的说明如下:

  • HTTPRequestMethod:请求方法。
  • CanonicalURI:请求的 URI。
  • CanonicalQueryString:规范的查询字符串。CanonicalQueryString 的值是通过以下规则生成的:
    • 按参数名称对查询参数进行升序排序。
  • CanonicalHeaders:规范的请求头。CanonicalHeaders 是通过以下步骤生成的:
    • 将参与签名计算的请求头参数的名称转化成小写字母。
    • 在字符级别对这些名称进行升序排序。
    • 以 key-value 的格式拼接这些请求头参数。key 是请求头参数的名称,value 是请求头参数的值。
    • 在每个请求头参数后添加换行符(\n)。
  • SignedHeaders:参见伪代码中参数的说明
  • HexEncode(Hash(RequestPayload)):一个计算值。该值是通过对请求正文中 payload 的值应用 SHA256 哈希算法计算得到的。

伪代码中参数的说明

以下表格包含了本文中多个伪代码中参数的说明。

参数名称数据类型参数说明示例
AccessKeystring您账号的 Access Key ID。参考获取 Access KeyAKLTMjI2ODVlYzI3ZGY1NGU4ZjhjYWRjMTlmNTM5OTZkYzE
RequestDatestring该参数与 X-Date 的定义相同。20210913T081805Z
ShortDatestring该参数与 RequestDate 的定义相同,只不过时间精度是日。20210913
Regionstring私网解析 PrivateZone 所在的地域。该参数的取值是 cn-north-1cn-north-1
Servicestring私网解析 PrivateZone 的服务名称。该参数的取值是 private_zoneprivate_zone
SignedHeadersstring参与签名计算的请求头参数。多个请求头参数使用分号(;)分隔。这些请求头参数是根据参数名称升序排序的。

一般来说,SignedHeaders 的值是 host;x-content-sha256;x-date。您也可以指定任意请求头参数作为 SignedHeaders 的值。
host;x-content-sha256;x-date
Algorithmstring表示签名计算所使用的算法。该参数的取值为 HMAC-SHA256HMAC-SHA256
Requeststring该参数是一个常量,值是 requestrequest