このトピックでは、リソース指向アーキテクチャ(ROA)形式で Alibaba Cloud API オペレーションを呼び出すための HTTP リクエストを送信する方法について説明します。
リクエスト構文と署名方式 V2 は廃止されました。リクエスト構文と署名方式 V3 を使用してください。
HTTP リクエスト構文
次の表は、完全な Alibaba Cloud API リクエストに含まれるパラメーターを示しています。
コンポーネント | 必須 | 説明 | 例 |
プロトコル | はい | リクエストプロトコル。API でサポートされているプロトコルは、API メタデータで確認できます。API が | https:// |
エンドポイント | はい | Alibaba Cloud サービス API のエンドポイント。各 Alibaba Cloud サービスのエンドポイントをリストしたトピックがあります。これらのトピックでは、さまざまなリージョンにおけるサービスのエンドポイントを確認できます。 | bailian.cn-beijing.aliyuncs.com |
resource_URI_parameters | はい | 操作の URL。URL には、API のパスと、パスおよびクエリコンポーネント内のリクエストパラメーターが含まれます。 | /{WorkspaceId}/datacenter/category |
RequestHeader | はい | リクエストヘッダー。ほとんどの場合、API バージョン番号、エンドポイント、認証情報などの情報が含まれます。詳細については、このトピックのRequestHeader のパラメーターセクションをご参照ください。 | Accept:application/json Authorization:acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0= Content-MD5:LP54yxk8n7KqF1PPgbJizw== Content-Type:application/json Date:Wed, 16 Apr 2025 03:44:46 GMT Host:bailian.cn-beijing.aliyuncs.com x-acs-signature-method:HMAC-SHA1 x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538 x-acs-signature-version:1.0 x-acs-version:2023-12-29 |
RequestBody | はい | リクエストボディ内の操作固有のパラメーター。詳細については、OpenAPI Explorer をご参照ください。 | {"CategoryName":"test","CategoryType":"UNSTRUCTURED"} |
HTTPMethod | はい | ROA API のリクエストメソッド。有効な値:PUT、POST、GET、DELETE。API でサポートされているリクエストメソッドは、API リファレンスで確認できます。 | POST |
RequestHeader のパラメーター
次の表は、完全な Alibaba Cloud API リクエストに含まれるパラメーターを示しています。
パラメーター | タイプ | 必須 | 説明 | 例 |
Accept | String | いいえ | 応答形式。このパラメーターを指定しない場合、応答は XML 形式になります。ROA 形式の API オペレーションを呼び出すには、値を | application/json |
Content-MD5 | String | いいえ | リクエストボディの Base64 エンコードされた 128 ビット MD5 ハッシュ値。 | LP54yxk8n7KqF1PPgbJizw== |
Date | String | はい | リクエストのタイムスタンプ。タイムスタンプは 15 分間有効です。タイムスタンプが生成されてから 15 分以内にリクエストを開始する必要があります。HTTP 1.1 に準拠して、GMT で時間を指定します。例:Tue 9 Apr 2019 07:35:29 GMT。 | Wed, 16 Apr 2025 03:44:46 GMT |
Host | String | はい | Alibaba Cloud サービス API のエンドポイント。詳細については、このトピックのHTTP リクエスト構造セクションをご参照ください。 | bailian.cn-beijing.aliyuncs.com |
x-acs-signature-method | String | リクエストが匿名でない場合は必須 | 署名文字列の暗号化方式。値を | HMAC-SHA1 |
x-acs-signature-nonce | String | はい | ネットワークリプレイ攻撃を防ぐために使用される一意の乱数。リプレイ攻撃を防ぐために、リクエストごとに異なる番号を使用することをお勧めします。 | ef34aae7-7bd2-413d-a541-680cd2c48538 |
x-acs-signature-version | String | いいえ | 署名バージョン。値を | 1.0 |
x-acs-version | String | はい | API のバージョン。OpenAPI Explorer または API メタデータ で API バージョンを取得できます。 | 2023-12-29 |
Authorization | String | リクエストが匿名でない場合は必須 | リクエストの有効性を検証するために使用される認証情報。 Signature パラメーターは、リクエストの署名文字列を指定します。詳細については、このトピックの署名方式セクションをご参照ください。 | acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY= |
署名方式
セキュリティを確保するために、すべての HTTP および HTTPS API リクエストに署名する必要があります。リクエストが Alibaba Cloud API ゲートウェイに到達すると、ゲートウェイはリクエストパラメーターとリクエストヘッダーに基づいて署名を再計算し、計算された署名とリクエスト内の署名を比較して API 呼び出し元の ID を検証します。このメカニズムにより、データの整合性とセキュリティが確保されます。このセクションでは、署名の計算方法について説明します。
すべてのリクエストとレスポンスは UTF-8 でエンコードされます。
ステップ 1:正規化ヘッダーを構築する
正規化ヘッダーは非標準 HTTP ヘッダーです。非標準 HTTP ヘッダーとは、リクエストで名前のプレフィックスが x-acs- であるヘッダーです。正規化ヘッダーを構築するには、次の手順を実行します。
リクエストヘッダー 内のプレフィックスが
x-acs-であるリクエストヘッダー名を小文字に変換し、辞書順に昇順でソートします。リクエストヘッダーの名前と値から先頭と末尾のスペースを削除します。
最後のヘッダーを含め、各ヘッダーの末尾に
\nデリミターを追加し、すべてのヘッダーを連結します。
例:
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29
ステップ 2:正規化リソースを構築する
正規化リソースは、アクセスするリソースの正規表現です。正規化リソースを構築するには、次の手順を実行します。
リクエストのクエリ文字列内のパラメーターを名前でアルファベット順にソートし、アンパサンド(
&)を使用してパラメーターを連結し、ソートされたクエリ文字列を生成します。疑問符(
?)を使用して、リクエストされたリソースパスとソートされたクエリ文字列を連結し、正規化リソースを取得します。リソースパスは、エンドポイントとクエリ文字列の間の部分です。パスには、エンドポイントの後のスラッシュ(/)が含まれますが、クエリ文字列の前の疑問符(?)は含まれません。リクエストにクエリ文字列が含まれていない場合は、リソースパスを正規化リソースとして使用します。
例:
/llm-p2e4XXXXXXXXsvtn/datacenter/categoryステップ 3:署名対象文字列を構築する
次の擬似コードに基づいて、署名対象文字列を作成します。
String stringToSign =
HTTPMethod + "\n" +
Accept + "\n" +
ContentMD5 + "\n" +
ContentType + "\n" +
Date + "\n" +
CanonicalizedHeaders +
CanonicalizedResourceパラメーター | 説明 |
HTTPMethod | POST や GET など、大文字の HTTP メソッド。 |
Accept | Accept ヘッダーの値。このヘッダーが存在しない場合は、空の文字列を設定します。 |
ContentMD5 | Content-MD5 ヘッダーの値。このヘッダーが存在しない場合は、空の文字列を設定します。 |
ContentType | Content-Type ヘッダーの値。このヘッダーが存在しない場合は、空の文字列を設定します。 説明 リクエストボディの MIME(Multipurpose Internet Mail Extensions)タイプ。 |
Date | Date ヘッダーの値。 |
CanonicalizedHeaders | このトピックのステップ 1:正規化ヘッダーを構築するから取得した正規化ヘッダー。 |
CanonicalizedResource | このトピックのステップ 2:正規化リソースを構築するから取得した正規化リソース。 |
例:
POST
application/json
LP54yxk8n7KqF1PPgbJizw==
application/json
Wed, 16 Apr 2025 03:44:46 GMT
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29
/llm-p2e4XXXXXXXXsvtn/datacenter/categoryステップ 4:署名文字列を計算する
HMAC-SHA1 アルゴリズムを使用して、署名対象文字列の HMAC(Hash-based Message Authentication Code)値を計算し、Base64 エンコードルールに基づいて HMAC 値を署名文字列にエンコードします。HMAC の詳細については、RFC 2104 をご参照ください。
String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))SigningKey パラメーターを AccessKey シークレットに設定します。詳細については、AccessKey ペアを作成するをご参照ください。
例:
s8ZMF/eAIvvPJwehLLha0bVNFJ0=ステップ 5:Authorization ヘッダーを指定する
署名文字列を取得したら、次のコードに基づいて Authorization ヘッダーの値を構築します。
String Authorization = "acs " + AccessKeyId + ":" + signature例:
acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=ステップ 6:リクエストに署名を追加し、リクエストを送信する
POST https://bailian.cn-beijing.aliyuncs.com/{WorkspaceId}/datacenter/category HTTP/1.1
Accept:application/json
Authorization:acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Content-MD5:LP54yxk8n7KqF1PPgbJizw==
Content-Type:application/json
Date:Wed, 16 Apr 2025 03:44:46 GMT
Host:bailian.cn-beijing.aliyuncs.com
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29
{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}
署名例
Java
この例では、JDK 1.8 ランタイム環境を使用しています。ビジネス要件に基づいてパラメーターを調整してください。
Java で署名メソッドを使用するには、次の Maven 依存関係を pom.xml ファイルに追加する必要があります。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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 javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
public class SignatureDemo {
public static class SignatureRequest {
private final String httpMethod;
private final String host;
private final String version;
private final String canonicalUri;
public TreeMap<String, String> headers = new TreeMap<>();
public TreeMap<String, Object> queryParams = new TreeMap<>();
public byte[] bodyByteArray;
public SignatureRequest(String httpMethod, String host, String version, String canonicalUri) {
this.httpMethod = httpMethod;
this.host = host;
this.version = version;
this.canonicalUri = canonicalUri;
initHeaders();
}
private void initHeaders() {
headers.put("Host", host);
headers.put("x-acs-version", version);
headers.put("x-acs-signature-version", "1.0");
headers.put("Accept", "application/json");
headers.put("x-acs-signature-nonce", java.util.UUID.randomUUID().toString());
headers.put("Date", getGmtDate());
headers.put("x-acs-signature-method", "HMAC-SHA1");
}
private String getGmtDate() {
SimpleDateFormat gmtFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.ENGLISH);
gmtFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return gmtFormat.format(new Date());
}
public void setBody(Map<String, String> body, ContentType contentType) {
Gson gson = (new GsonBuilder()).disableHtmlEscaping().create();
this.bodyByteArray = gson.toJson(body).getBytes(StandardCharsets.UTF_8);
headers.put("Content-MD5", md5Sum(this.bodyByteArray));
headers.put("Content-Type", contentType.getMimeType());
}
}
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");
private static final String ALGORITHM_NAME = "HmacSHA1";
public static void main(String[] args) {
// 例 1:POST リクエストを送信します。
String method = "POST";
String host = "bailian.cn-beijing.aliyuncs.com";
String version = "2023-12-29";
String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/category";
SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);
Map<String, String> body = new HashMap<>();
body.put("CategoryName", "test");
body.put("CategoryType", "UNSTRUCTURED");
System.out.println(new Gson().toJson(body));
signatureRequest.setBody(body, ContentType.APPLICATION_JSON);
/*// 例 2:GET リクエストを送信します。
String method = "GET";
String host = "bailian.cn-beijing.aliyuncs.com";
String version = "2023-12-29";
String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/files";
SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);
signatureRequest.queryParams.put("CategoryId", "cate_a946*********************_10045991");*/
/*// 例 3:DELETE リクエストを送信します。
String method = "DELETE";
String host = "bailian.cn-beijing.aliyuncs.com";
String version = "2023-12-29";
String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/category/cate_a946*********************_10045991";
SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);*/
// Authorization ヘッダーの値を構築します。
generateSignature(signatureRequest);
// HTTPClient を使用してリクエストを送信します。
callApi(signatureRequest);
}
private static void generateSignature(SignatureRequest signatureRequest) {
try {
// ステップ 1:正規化されたヘッダーを構築する
String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
// ステップ 2:正規化されたリソースを構築する
String canonicalQueryString = buildQueryString(signatureRequest);
// ステップ 3:署名対象の文字列を構築する
String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
// ステップ 4:署名文字列を計算する
String signature = signString(stringToSign);
// ステップ 5:Authorization ヘッダーを指定します。
String authorization = "acs " + ACCESS_KEY_ID + ":" + signature;
signatureRequest.headers.put("Authorization", authorization);
} catch (Exception ex) {
throw new IllegalArgumentException(ex.toString());
}
}
private static void callApi(SignatureRequest signatureRequest) {
try {
// HttpClient を使用してリクエストを送信します。
String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
URIBuilder uriBuilder = new URIBuilder(url);
// リクエスト パラメーターを指定します。
signatureRequest.queryParams.forEach((key, value) -> uriBuilder.addParameter(key, String.valueOf(value)));
HttpUriRequest httpRequest;
switch (signatureRequest.httpMethod) {
case "GET":
httpRequest = new HttpGet(uriBuilder.build());
break;
case "POST":
HttpPost httpPost = new HttpPost(uriBuilder.build());
if (signatureRequest.bodyByteArray != null) {
httpPost.setEntity(new ByteArrayEntity(signatureRequest.bodyByteArray, ContentType.create(signatureRequest.headers.get("Content-Type"))));
}
httpRequest = httpPost;
break;
case "PUT":
HttpPut httpPut = new HttpPut(uriBuilder.build());
if (signatureRequest.bodyByteArray != null) {
httpPut.setEntity(new ByteArrayEntity(signatureRequest.bodyByteArray, ContentType.create(signatureRequest.headers.get("Content-Type"))));
}
httpRequest = httpPut;
break;
case "DELETE":
httpRequest = new HttpDelete(uriBuilder.build());
break;
default:
System.out.println("サポートされていない HTTP メソッド: " + signatureRequest.httpMethod);
throw new IllegalArgumentException("サポートされていない HTTP メソッド");
}
// HTTP リクエスト ヘッダーを指定します。
signatureRequest.headers.forEach(httpRequest::addHeader);
// リクエストを送信します。
try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(httpRequest)) {
String result = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println(result);
} catch (IOException e) {
// エラーを処理します。
System.out.println("リクエストの送信に失敗しました");
throw new IllegalArgumentException(e.toString());
}
} catch (URISyntaxException e) {
// エラーを処理します。
System.out.println("無効な URI 構文");
throw new IllegalArgumentException(e.toString());
}
}
private static String buildCanonicalHeaders(SignatureRequest signatureRequest) {
StringBuilder canonicalHeaders = new StringBuilder();
signatureRequest.headers.entrySet().stream()
.filter(entry -> entry.getKey().startsWith("x-acs-"))
.forEach(entry -> canonicalHeaders.append(entry.getKey().toLowerCase().trim()).append(":").append(entry.getValue().trim()).append("\n"));
return canonicalHeaders.toString();
}
private static String buildQueryString(SignatureRequest signatureRequest) {
StringBuilder queryBuilder = new StringBuilder(signatureRequest.canonicalUri);
if (!signatureRequest.queryParams.isEmpty()) {
queryBuilder.append("?");
}
for (Map.Entry<String, Object> entry : signatureRequest.queryParams.entrySet()) {
queryBuilder.append(entry.getKey());
String value = (String) entry.getValue();
if (value != null && !value.isEmpty()) {
queryBuilder.append("=").append(value).append("&");
} else {
queryBuilder.append("&");
}
}
String queryString = queryBuilder.toString();
if (queryString.endsWith("&")) {
queryString = queryString.substring(0, queryString.length() - 1);
}
return queryString;
}
private static String buildStringToSign(SignatureRequest signatureRequest, String canonicalQueryString, String canonicalHeaders) {
StringBuilder sb = new StringBuilder();
sb.append(signatureRequest.httpMethod).append("\n");
appendIfPresent(sb, signatureRequest.headers.get("Accept"));
appendIfPresent(sb, signatureRequest.headers.get("Content-MD5"));
appendIfPresent(sb, signatureRequest.headers.get("Content-Type"));
appendIfPresent(sb, signatureRequest.headers.get("Date"));
sb.append(canonicalHeaders);
sb.append(canonicalQueryString);
return sb.toString();
}
private static void appendIfPresent(StringBuilder sb, String value) {
if (value != null) {
sb.append(value).append("\n");
} else {
sb.append("\n");
}
}
private static String signString(String stringToSign) {
try {
Mac mac = Mac.getInstance(ALGORITHM_NAME);
mac.init(new SecretKeySpec(ACCESS_KEY_SECRET.getBytes(StandardCharsets.UTF_8), ALGORITHM_NAME));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printBase64Binary(signData);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
throw new IllegalArgumentException(ex.toString());
}
}
public static String md5Sum(byte[] buff) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5Bytes = md.digest(buff);
return DatatypeConverter.printBase64Binary(md5Bytes);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalArgumentException(ex.toString());
}
}
}
参照資料
RPC スタイル API と ROA スタイル API の違いの詳細については、「API スタイル」をご参照ください。