Envoyez des requêtes HTTP pour appeler des opérations d'API RPC Alibaba Cloud en calculant une signature V2.
La syntaxe de requête et la méthode de signature V2 sont obsolètes. Utilisez la syntaxe de requête et méthode de signature V3.
Syntaxe de requête HTTP
Une requête d'API RPC Alibaba Cloud comprend les éléments suivants :
|
Composant |
Obligatoire |
Description |
Exemple |
|
Protocole |
Oui |
Protocole de la requête. Consultez les protocoles pris en charge dans les métadonnées OpenAPI. Si |
https:// |
|
Endpoint |
Oui |
Endpoint du service. Reportez-vous à la référence d'API de chaque service pour obtenir les endpoints spécifiques à chaque région. |
ecs.cn-hangzhou.aliyuncs.com |
|
Paramètres de requête communs |
Oui |
Paramètres obligatoires dans toutes les requêtes d'API Alibaba Cloud. Pour plus d'informations, consultez la section Paramètres de requête communs de cette rubrique. |
Action |
|
Paramètres de requête spécifiques à l'opération |
Non |
Paramètres propres à l'opération d'API appelée. Consultez ces paramètres dans les métadonnées OpenAPI ou sur le OpenAPI Explorer. |
RegionId |
|
HTTPMethod |
Oui |
Méthode de requête. Les méthodes prises en charge par l'API figurent dans les métadonnées OpenAPI. |
GET |
Paramètres de requête communs
Chaque requête d'API doit inclure les paramètres suivants :
|
Paramètre |
Type |
Obligatoire |
Description |
Exemple |
|
Action |
String |
Oui |
Opération d'API à appeler. Retrouvez les opérations disponibles sur le OpenAPI Explorer. |
CreateInstance |
|
Version |
String |
Oui |
Version de l'API. Vérifiez la version correspondant au service cible sur le OpenAPI Explorer. Par exemple, la version de l'API Short Message Service (SMS) est 2017-05-25. |
2014-05-26 |
|
Format |
String |
Non |
Format de réponse. Valeurs valides : JSON, XML. Valeur par défaut : XML. |
JSON |
|
AccessKeyId |
String |
Oui |
Votre AccessKey ID Alibaba Cloud. Consultez-le dans la console Resource Access Management (RAM). Pour plus d'informations sur la création d'une paire d'AccessKey, reportez-vous à Créer une AccessKey. |
yourAccessKeyId |
|
SignatureNonce |
String |
Oui |
Nombre aléatoire unique destiné à prévenir les attaques par rejeu. Utilisez une valeur différente pour chaque requête. Aucune limite de longueur n'est imposée. |
15215528852396 |
|
Timestamp |
String |
Oui |
Horodatage de la requête au format ISO 8601 (aaaa-MM-jjTHH:mm:ssZ). Valable pendant 31 minutes. Exemple : |
2018-01-01T12:00:00Z |
|
SignatureMethod |
String |
Oui |
Algorithme de signature. Définissez cette valeur sur |
HMAC-SHA1 |
|
SignatureVersion |
String |
Oui |
Version de l'algorithme de signature. Définissez cette valeur sur |
1.0 |
|
Signature |
String |
Oui |
Signature de la requête calculée selon la section Signatures ci-dessous. |
Pc5WB8gokVn0xfeu%2FZV%2BiNM1dgI%3D |
Transmission des paramètres
Dans les métadonnées OpenAPI, le champ in indique comment transmettre chaque paramètre dans la requête.
|
Position |
Description |
content-type |
|
"in": "query" |
Les paramètres de requête apparaissent dans l'URL après le caractère |
Facultatif. Si ce paramètre est fourni, la valeur doit être |
|
"in": "formData" |
Les paramètres de formulaire sont envoyés dans le corps de la requête sous la forme |
Obligatoire. La valeur doit être application/x-www-form-urlencoded. |
|
"in": "body" |
Les paramètres du corps sont transmis directement dans le corps de la requête. |
Obligatoire. La valeur de Content-Type dépend du type de contenu envoyé. Par exemple :
|
L'ordre des paramètres dans une chaîne JSON n'affecte pas le calcul de la signature.
Méthode de signature
Toutes les requêtes d'API doivent être signées. Alibaba Cloud vérifie l'identité de l'appelant à l'aide de la signature de la requête.
Étape 1 : Construire une chaîne de requête canonique
1. Triez tous les paramètres par ordre alphabétique selon leur clé (à l'exclusion de Signature), en combinant les paramètres de requête communs et les paramètres spécifiques à l'opération. Pseudo-code :
// Concatenate the common request parameters and operation-specific parameters in alphabetical order of parameter keys.
params = merged(publicParams,apiReuqestParams)
sortParams = sorted(params.keys())
2. Encodez les clés et valeurs de sortParams en UTF-8 conformément à la norme RFC 3986, puis associez chaque paire avec le caractère =.
Règles d'encodage :
Les lettres majuscules, minuscules, chiffres, tirets
(-), traits de soulignement(_), points(.)et tildes(~)ne nécessitent aucun encodage.-
Les autres caractères ASCII doivent être encodés au format %XY. XY représente le code ASCII des caractères en notation hexadécimale. Par exemple, les guillemets doubles (
") sont encodés sous la forme%22. Le tableau suivant décrit certains caractères spéciaux avant et après l'encodage. Prêtez une attention particulière à ces caractères.Avant l'encodage
Après l'encodage
Espaces ()
%20Astérisques (
*)%2A%7ETildes (
~)
Pseudo-code :
encodeURIComponentParam = encodeURIComponent(sortParams.key) + "=" + encodeURIComponent(sortParams.value)
3. Assemblez les paires clé=valeur encodées avec le caractère & pour former la canonicalized query string (en respectant le même ordre de tri qu'à l'étape 1). Pseudo-code :
CanonicalizedQueryString = encodeURIComponentParam1 + "&" + encodeURIComponentParam2 + ... + encodeURIComponentParamN
Étape 2 : Construire la chaîne à signer et calculer la signature
Construisez la chaîne StringToSign. Pseudo-code :
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.
Étape 3 : Calculer la signature
Calculez la signature à l'aide de la string-to-sign et de l'AccessKey secret conformément à la norme RFC 2104 (HMAC-SHA1). Pseudo-code :
signature = Base64(HMAC_SHA1(AccessKeySecret + "&", UTF_8_Encoding_Of(stringToSign)))
Où :
Base64() : fonction d'encodage.
HMAC_SHA1() : retourne des données binaires brutes (et non une chaîne hexadécimale).
UTF_8_Encoding_Of() : encodeur UTF-8.
Étape 4 : Ajouter la signature à la requête
Encodez la signature dans l'URL conformément à la norme RFC 3986 et ajoutez-la en tant que paramètre Signature. Format final de l'URL :
https://endpoint/?sortParams.key1=sortParams.value1&sortParams.key2=sortParams.value2&...&sortParams.keyN=sortParams.valueN&Signature=signature
Exemples de signature
Fixed parameter values
Cet exemple appelle l'opération DescribeDedicatedHosts d'ECS pour interroger les détails des hôtes dédiés. Utilisez les valeurs d'exemple ci-dessous pour vérifier votre implémentation de signature.
|
Paramètre |
Valeur d'exemple |
|
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 |
Paramètres de requête spécifiques à l'opération
|
Paramètre |
Valeur d'exemple |
|
RegionId |
cn-beijing |
Étapes de calcul de la signature :
-
Construisez une chaîne de requête canonique.
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 -
Construisez la
string-to-sign.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 -
Calculez la signature. Dans cet exemple, l'AccessKey secret est
testsecret. Signature obtenue :9NaGiOspFP5UPcwX8Iwt2YJXXuk= -
Envoyez la requête. Obtenez l'URL complète au format
[protocol][endpoint]?[common request parameters][operation-specific parameters].https://ecs.cn-beijing.aliyuncs.com/?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-beijingEnvoyez cette requête à l'aide de cURL ou Wget pour appeler l'opération
DescribeDedicatedHosts.
Java
Cet exemple utilise Java 8. Ajustez les paramètres selon vos besoins.
Ajoutez la dépendance Maven suivante au fichier pom.xml :
<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 by 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
Cet exemple utilise Python 3.12.3. Ajustez les paramètres selon vos besoins.
Pour exécuter l'exemple de code, installez la bibliothèque requests :
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 to construct and manage RPC API requests.
"""
def __init__(self, http_method: str, host: str, action: str, version: str):
"""
Initializes the SignatureRequest object.
Args:
http_method: The HTTP request method (such as GET and POST).
host: The API service endpoint.
action: The API operation name.
version: The API version.
"""
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 headers.
self.query_params: Dict[str, Any] = OrderedDict() # The query parameters.
self.body: Dict[str, Any] = OrderedDict() # The equest body.
self.body_byte: bytes = b"" # The bytes of the request body.
self.all_params: Dict[str, Any] = OrderedDict() # A collection of all parameters.
self.set_headers()
def set_headers(self) -> None:
"""
Sets the required common headers for an RPC request.
"""
"AccessKeyId": "<ak id>", //The AccessKey ID.
->format('JSON') // Specifies the response format.
self.headers["SignatureMethod"] = "HMAC-SHA1" # The signature algorithm.
self.headers["SignatureVersion"] = "1.0" # The signature version.
self.headers["SignatureNonce"] = "{" + str(uuid.uuid4()) + "}" # The random anti-replay string.
self.headers["Timestamp"] = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ") # The timestamp.
self.headers["Action"] =self.action# The API name.
self.headers["Version"] =self.version# The API version number.
def set_content_type(self, content_type):
self.headers["Content-Type"] = content_type
def get_all_params(self) -> None:
"""
Collect and sort 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 parameters by key in ASCII order.
self.all_params = OrderedDict(sorted(self.all_params.items()))
def calculate_signature(signature_request: SignatureRequest) -> None:
"""
Calculate the signature of an RPC request
Args:
signature_request: The SignatureRequest object.
"""
signature_request.get_all_params() # Collect and sort all request parameters.
# Construct a canonicalized 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}")
# Construct a string-to-sign: HTTP method + canonical URI + canonicalized 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 parameter.
def form_data_to_string(form_data: Dict[str, Any]) -> str:
"""
Convert a form data dictionary to a URL-encoded string.
Args:
form_data: The form data dictionary.
Returns:
A URL-encoded string.
"""
tile_map: Dict[str, Any] = {}
def process_object(key: str, value: Any) -> None:
"""
Recursively flattens nested structures.
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 dot.
clean_key = key[1:] if key.startswith(".") else key
# Process byte data and regular 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 join.
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:
"""
Generate a signature using the HMAC-SHA1 algorithm.
Args:
access_key_secret: The AccessKey secret.
string_to_sign: The string to be signed.
Returns:
A Base64-encoded signature.
"""
try:
# he 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 result.
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 according to RFC 3986.
Args:
s: The string to encode.
Returns:
The encoded string.
"""
if s is None:
raise ValueError("Input string cannot be None")
# URL-encode using UTF-8, but keep tildes (~) unencoded.
encoded = urllib.parse.quote(s.encode("utf-8"), safe=b"~")
# Replace special character encodings.
return encoded.replace("+", "%20").replace("*", "%2A")
def call_api(signature_request: SignatureRequest) -> None:
"""
Send the API request.
"""
url = f"https://{signature_request.host}/"
# Construct request parameters.
params = {k: str(v) for k, v in signature_request.all_params.items()}
# Prepare request arguments.
request_kwargs = {
"params": params
}
# Add request body data (if any).
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 uniformly.
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: Request with no body. Content-Type is optional. If passed, use application/json.
signature_request = SignatureRequest(
http_method="POST",
host="dysmsapi.aliyuncs.com",
action="SendSms",
version="2017-05-25"
)
# query_params are used 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: Request with a form data body. Content-Type must be application/x-www-form-urlencoded.
"""
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. Content-Type must be 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)
# Send the API request.
call_api(signature_request)