All Products
Search
Document Center

Alibaba Cloud SDK:Request syntax and signature method V2 for ROA-style APIs

Last Updated:May 09, 2025

This topic describes how to send HTTP requests to call Alibaba Cloud API operations in the resource-oriented architecture (ROA) style.

Important

The request syntax and signature method V2 are discontinued. Use the request syntax and signature method V3.

HTTP request syntax

The following table describes the parameters that are included in a complete Alibaba Cloud API request.

Component

Required

Description

Example

Protocol

Yes

The request protocol. You can view the protocols supported by the API in the API metadata. If the API supports both HTTP and HTTPS, we recommend that you use HTTPS for higher security.

https://

Endpoint

Yes

The endpoint of the Alibaba Cloud service API. Topics that list the endpoints of each Alibaba Cloud service are available. You can view the endpoints of a service in different regions in these topics.

bailian.cn-beijing.aliyuncs.com

resource_URI_parameters

Yes

The URL of the operation. The URL contains the path of the API and the request parameters in the path and query components.

/{WorkspaceId}/datacenter/category

RequestHeader

Yes

The request headers. In most cases, information such as the API version number, endpoint, and authentication information is included. For more information, see the Parameters for RequestHeader section of this topic.

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

Yes

The operation-specific parameters in the request body. For more information, visit OpenAPI Explorer.

{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}

HTTPMethod

Yes

The request method of an ROA API. Valid values: PUT, POST, GET, and DELETE. You can view the request methods supported by the API in the API reference.

POST

Parameters for RequestHeader

The following table describes the parameters that are included in a complete Alibaba Cloud API request.

Parameter

Type

Required

Description

Example

Accept

String

No

The response format. If you do not specify this parameter, the response is in the XML format. To call an ROA-style API operation, set the value to application/json.

application/json

Content-MD5

String

No

The Base64-encoded 128-bit MD5 hash value of the request body.

LP54yxk8n7KqF1PPgbJizw==

Date

String

Yes

The timestamp of the request. The timestamp is valid for 15 minutes. You must initiate the request within 15 minutes after the timestamp is generated. Specify the time in GMT in compliance with HTTP 1.1. Example: Tue 9 Apr 2019 07:35:29 GMT.

Wed, 16 Apr 2025 03:44:46 GMT

Host

String

Yes

The endpoint of the Alibaba Cloud service API. For more information, see the HTTP request structure section of this topic.

bailian.cn-beijing.aliyuncs.com

x-acs-signature-method

String

Yes if the request is non-anonymous

The encryption method of the signature string. Set the value to HMAC-SHA1.

HMAC-SHA1

x-acs-signature-nonce

String

Yes

A unique, random number used to prevent network replay attacks. To prevent replay attacks, we recommend that you use different numbers for different requests.

ef34aae7-7bd2-413d-a541-680cd2c48538

x-acs-signature-version

String

No

The signature version. Set the value to 1.0.

1.0

x-acs-version

String

Yes

The version of the API. You can obtain the API version in OpenAPI Explorer or in the API metadata.

2023-12-29

Authorization

String

Yes if the request is non-anonymous

The authentication information that is used to verify the validity of the request. Specify the value in the AccessKeyId:Signature format. The AccessKeyId parameter specifies the AccessKey ID provided to you by Alibaba Cloud. You can view your AccessKey ID in the Resource Access Management (RAM) console. For information about how to create an AccessKey pair, see Obtain an AccessKey pair.

The Signature parameter specifies the signature string of the request. For more information, see the Signature method section of this topic.

acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY=

Signature method

You must sign all HTTP and HTTPS API requests to ensure security. When a request reaches the Alibaba Cloud API gateway, the gateway recalculates the signature based on the request parameters and request header, and then compares the calculated signature with the signature in the request to verify the identity of the API caller. This mechanism ensures data integrity and security. This section describes how to calculate signatures.

Note

All requests and responses are encoded in UTF-8.

Step 1: Construct a canonicalized header

Canonicalized headers are non-standard HTTP headers Non-standard HTTP headers are headers whose names are prefixed with x-acs- in the request. To construct canonicalized headers, perform the following steps:

  1. Convert the request header names prefixed with x-acs- in the request header to lowercase letters and sort them in ascending lexicographical order.

  2. Remove the leading and trailing spaces from the name and value of the request header.

  3. Append the \n delimiter to the end of each header, including the last header, and concatenate all the headers.

Example:

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

Step 2: Construct a canonicalized resource

Canonicalized resources are the canonical descriptions of the resources that you want to access. You can perform the following steps to construct canonicalized resources:

  1. Sort the parameters in the query string of the request by name in alphabetical order and concatenate the parameters by using ampersands (&) to generate the sorted query string.

  2. Concatenate the requested resource path with the sorted query string by using a question mark (?) to obtain canonicalized resources. The resource path is the part between the endpoint and the query string. The path includes the forward slash (/) following the endpoint but excludes the question mark (?) preceding the query string. If the request does not contain a query string, use the resource path as the canonicalized resource.

Example:

/llm-p2e4XXXXXXXXsvtn/datacenter/category

Step 3: Construct a string-to-sign

Create a string-to-sign based on the following pseudocode:

String stringToSign = 
    HTTPMethod + "\n" +
    Accept + "\n" +
    ContentMD5 + "\n" +
    ContentType + "\n" +
    Date + "\n" +
    CanonicalizedHeaders +
    CanonicalizedResource

Parameter

Description

HTTPMethod

The HTTP method in uppercase letters, such as POST or GET.

Accept

The value of the Accept header. Set the value to an empty string if this header does not exist.

ContentMD5

The value of the Content-MD5 header. Set the value to an empty string if this header does not exist.

ContentType

The value of the Content-Type header. Set the value to an empty string if this header does not exist.

Note

The Multipurpose Internet Mail Extensions (MIME) type of the request body.

Date

The value of the Date header.

CanonicalizedHeaders

The canonicalized headers that are obtained from Step 1: Construct canonicalized headers of this topic.

CanonicalizedResource

The canonicalized resources that are obtained from Step 2: Construct canonicalized resources of this topic.

Example:

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

Step 4: Calculate the signature string

Calculate the hash-based message authentication code (HMAC) value of the string-to-sign by using the HMAC-SHA1 algorithm and encode the HMAC value into a signature string based on Base64 encoding rules. For more information about HMAC, see RFC 2104.

String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
Important

Set the SigningKey parameter to your AccessKey secret. For more information, see Create an AccessKey pair.

Example:

s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Step 5: Specify the Authorization header.

After you obtain the signature string, construct the value of the Authorization header based on the following code:

String Authorization = "acs " + AccessKeyId + ":" + signature

Example:

acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Step 6: Add the signature to the request and send the request.

POST https://bailian.cn-beijing.aliyuncs.com/llm-p2e4XXXXXXXXsvtn/datacenter/category HTTP/1.1
 
Accept:application/json
Authorization:acs YourAccessKeyId:r8Y9ZqVhTrYGl4nieqk7CW0Pwow=
Content-MD5:LP54yxk8n7KqF1PPgbJizw==
Content-Type:application/json
Date:Wed, 16 Apr 2025 06:47:10 GMT
Host:bailian.cn-beijing.aliyuncs.com
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:e3d8efa7-b1d8-42f3-9733-4fe2691e15dc
x-acs-signature-version:1.0
x-acs-version:2023-12-29

{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}

Signature examples

Java

Note

In this example, the JDK 1.8 runtime environment is used. Adjust the parameters based on your business requirements.

To use the signature method in Java, you must add the following Maven dependencies to the pom.xml file:

<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) {
        // Example 1: Send a POST request.
        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);

        /*// Example 2: Send a GET request.
        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");*/

        /*// Example 3: Send a DELETE request.
        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);*/

        // Construct the value of the Authorization header.
        generateSignature(signatureRequest);
        // Use HTTPClient to send the request.
        callApi(signatureRequest);
    }

    private static void generateSignature(SignatureRequest signatureRequest) {
        try {
            // Step 1: Construct a canonicalized header
            String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
            // Step 2: Construct a canonicalized resource
            String canonicalQueryString = buildQueryString(signatureRequest);
            // Step 3: Construct a string-to-sign
            String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
            // Step 4: Calculate the signature string
            String signature = signString(stringToSign);
            // Step 5: Specify the Authorization header.
            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 {
            // Send the request by using HttpClient.
            String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
            URIBuilder uriBuilder = new URIBuilder(url);
            // Specify request parameters.
            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("Unsupported HTTP method: " + signatureRequest.httpMethod);
                    throw new IllegalArgumentException("Unsupported HTTP method");
            }
            // Specify HTTP request headers.
            signatureRequest.headers.forEach(httpRequest::addHeader);
            // Send the request.
            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) {
                // Handle errors.
                System.out.println("Failed to send request");
                throw new IllegalArgumentException(e.toString());
            }
        } catch (URISyntaxException e) {
            // Handle errors.
            System.out.println("Invalid URI syntax");
            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());
        }
    }
}

References

For more information about the difference between RPC-style APIs and ROA-style APIs, see API styles.