LoginSignup
0
2

More than 1 year has passed since last update.

Azure API Management から直接 Azure Cosmos DB の REST API をリクエストしてみた

Posted at

背景と目的

Cosmos DB にアクセスする API アプリを App Service や Functions で用意しおき、その API を API Management から使用するのが一般的だと思います。でもよくよく考えると Cosmos DB にはデータプレーン用の REST API が備わっており JSON がレスポンスされます。だったら、API Management から直接 Cosmos DB と連携した方が汎用的に使い回せて良いのではないかと思い、実際に試してみました。

前提条件

検証コマンドの実施環境は、Mac + Azure CLI です。

zsh
% sw_vers
ProductName:    macOS
ProductVersion: 11.4
BuildVersion:   20F71

% az version
{
  "azure-cli": "2.25.0",
  "azure-cli-core": "2.25.0",
  "azure-cli-telemetry": "1.0.6",
  "extensions": {}
}

実施内容

リソースグループと Cosmos DB を作成

zsh
# 環境変数とセットします
region=japaneast
prefix=mnrapimcdb

# リソースグループを作成します
az group create \
  --name ${prefix}-rg \
  --location $region

# Cosmos DB を作成します
az cosmosdb create \
  --name ${prefix}-cdb \
  --resource-group ${prefix}-rg

Cosmos DB のサンプル Python アプリで動作確認

zsh
git clone https://github.com/Azure-Samples/azure-cosmos-db-python-getting-started.git

cd azure-cosmos-db-python-getting-started/

cdbendpoint=$(az cosmosdb show \
  --name ${prefix}-cdb \
  --resource-group ${prefix}-rg \
  --query documentEndpoint \
  --output tsv)

cdbprimarykey=$(az cosmosdb keys list \
  --name ${prefix}-cdb \
  --resource-group ${prefix}-rg \
  --query primaryMasterKey \
  --output tsv)

sed -ie "s#\"endpoint\"#\"$cdbendpoint\"#" cosmos_get_started.py

sed -ie "s#\'primary_key'#'$cdbprimarykey'#" cosmos_get_started.py

python3 -m venv .venv

. .venv/bin/activate

pip install azure-cosmos

python cosmos_get_started.py

deactivate

cd ..

API Management を作成

zsh
#  API Management を作成します
az apim create \
  --name ${prefix}-apim \
  --resource-group ${prefix}-rg \
  --sku-name Consumption \
  --publisher-email email@mydomain.com \
  --publisher-name Microsoft

# Blank API を作成します
az apim api create \
  --service-name ${prefix}-apim \
  --resource-group ${prefix}-rg \
  --api-id MyApi \
  --path '/myapi' \
  --display-name 'My API'

# API 操作を作成します
az apim api operation create \
  --service-name ${prefix}-apim \
  --resource-group ${prefix}-rg \
  --api-id MyApi \
  --url-template "/getitem/{container}/{lastname}" \
  --method "GET" \
  --display-name GetContainerItem \
  --template-parameters name=container required="true" type="string" \
  --template-parameters name=lastname required="true" type="string"

# 名前付きの値を作成します:cdbendpoint
az apim nv create \
  --service-name ${prefix}-apim \
  --resource-group ${prefix}-rg \
  --named-value-id cdbendpoint \
  --display-name 'CosmosDB-Endpoint' \
  --value $cdbendpoint

# 名前付きの値を作成します:cosmosdbkey
az apim nv create \
  --service-name ${prefix}-apim \
  --resource-group ${prefix}-rg \
  --named-value-id cosmosdbkey \
  --display-name 'CosmosDB-Key' \
  --value $cdbprimarykey

作成した MyApi の Policy を登録

下記の XML を管理ポータル上で MyApi の Policy に登録する。

xml
<policies>
    <inbound>
        <base />
        <set-variable name="requestDateString" value="@(DateTime.UtcNow.ToString("r"))" />
        <set-variable name="container" value="@(context.Request.MatchedParameters["container"])" />
        <set-variable name="lastname" value="@(context.Request.MatchedParameters["lastname"])" />
        <send-request mode="new" response-variable-name="response" timeout="10" ignore-error="false">
            <set-url>@("{{CosmosDB-Endpoint}}/dbs/AzureSampleFamilyDatabase/colls/" + (string)context.Variables["container"] + "/docs")</set-url>
            <set-method>POST</set-method>
            <set-header name="Authorization" exists-action="override">
                <value>@{
                    var verb = "post";
                    var resourceType = "docs";
                    var resourceLink = "dbs/AzureSampleFamilyDatabase/colls/" + (string)context.Variables["container"];
                    var key = "{{CosmosDB-Key}}";
                    var keyType = "master";
                    var tokenVersion = "1.0";
                    var date = context.Variables.GetValueOrDefault<string>("requestDateString");
                    var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };  

                    verb = verb ?? "";  
                    resourceType = resourceType ?? "";
                    resourceLink = resourceLink ?? "";
                    string payLoad = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n",  
                            verb.ToLowerInvariant(),  
                            resourceType.ToLowerInvariant(),  
                            resourceLink,  
                            date.ToLowerInvariant(),  
                            ""  
                    );  
                    byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));  
                    string signature = Convert.ToBase64String(hashPayLoad);  

                    return System.Uri.EscapeDataString(String.Format("type={0}&ver={1}&sig={2}",  
                        keyType,  
                        tokenVersion,  
                        signature));
                }</value>
            </set-header>
            <set-header name="Content-Type" exists-action="override">
                <value>application/query+json</value>
            </set-header>
            <set-header name="x-ms-documentdb-isquery" exists-action="override">
                <value>True</value>
            </set-header>
            <set-header name="x-ms-documentdb-query-enablecrosspartition" exists-action="override">
                <value>True</value>
            </set-header>
            <set-header name="x-ms-date" exists-action="override">
                <value>@(context.Variables.GetValueOrDefault<string>("requestDateString"))</value>
            </set-header>
            <set-header name="x-ms-version" exists-action="override">
                <value>2018-12-31</value>
            </set-header>
            <set-header name="x-ms-query-enable-crosspartition" exists-action="override">
                <value>true</value>
            </set-header>
            <set-body>@("{\"query\": \"SELECT * FROM c WHERE c.lastName = @lastName\", " +
          "\"parameters\": [{ \"name\": \"@lastName\", \"value\": \"" + (string)context.Variables["lastname"] + "\"}]}")</set-body>
        </send-request>
        <return-response response-variable-name="existing response variable">
            <set-status code="200" reason="OK" />
            <set-header name="Content-Type" exists-action="override">
                <value>application/json</value>
            </set-header>
            <set-body>@(new JObject(new JProperty("response",((IResponse)context.Variables["response"]).Body.As<JObject>())).ToString())</set-body>
        </return-response>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

実施結果

API Management 経由で Cosmos DB の JSON を取得できるか試してみます。

zsh
# API Management のシークレットキーを取得します
apimkey=$(az rest \
  --method post \
  --uri "https://management.azure.com/subscriptions/$subid/resourceGroups/${prefix}-rg/providers/Microsoft.ApiManagement/service/${prefix}-apim/subscriptions/master/listSecrets?api-version=2019-12-01" \
  --query primaryKey \
  --output tsv)

# FamilyContainer の lastName = Smith をリクエストします 
curl -s https://${prefix}-apim.azure-api.net/myapi/getitem/FamilyContainer/Smith \
  -H "Ocp-Apim-Subscription-Key: $apimkey"

# lastName = Smith の Item を取得できました
{
  "response": {
    "_rid": "c8Y7AKAgb44=",
    "Documents": [
      {
        "id": "Smith_0961f8ce-93a9-41ea-b610-1cf0b3dfb496",
        "lastName": "Smith",
        "parents": null,
        "children": null,
        "address": {
          "state": "WA",
          "city": "Redmond"
        },
        "registered": true,
        "_rid": "c8Y7AKAgb44CAAAAAAAAAA==",
        "_self": "dbs/c8Y7AA==/colls/c8Y7AKAgb44=/docs/c8Y7AKAgb44CAAAAAAAAAA==/",
        "_etag": "\"020033b8-0000-2300-0000-60def6660000\"",
        "_attachments": "attachments/",
        "_ts": 1625224806
      }
    ],
    "_count": 1
  }
}

# FamilyContainer の lastName = Andersen をリクエストします 
curl -s https://${prefix}-apim.azure-api.net/myapi/getitem/FamilyContainer/Andersen \
  -H "Ocp-Apim-Subscription-Key: $apimkey"

# lastName = Andersen の Item を取得できました
{
  "response": {
    "_rid": "c8Y7AKAgb44=",
    "Documents": [
      {
        "id": "Andersen_5feb3d2c-f103-43f9-925f-8a97995e2aff",
        "lastName": "Andersen",
        "district": "WA5",
        "parents": [
          {
            "familyName": null,
            "firstName": "Thomas"
          },
          {
            "familyName": null,
            "firstName": "Mary Kay"
          }
        ],
        "children": null,
        "address": {
          "state": "WA",
          "county": "King",
          "city": "Seattle"
        },
        "registered": true,
        "_rid": "c8Y7AKAgb44BAAAAAAAAAA==",
        "_self": "dbs/c8Y7AA==/colls/c8Y7AKAgb44=/docs/c8Y7AKAgb44BAAAAAAAAAA==/",
        "_etag": "\"020032b8-0000-2300-0000-60def6660000\"",
        "_attachments": "attachments/",
        "_ts": 1625224806
      }
    ],
    "_count": 1
  }
}

参考

検証が済んだら後片付け。

zsh
# リソースグループを削除します
az group delete \
  --name ${prefix}-rg

データベースを取得するREST API例

Authorization headerの作成例

Api Policy - Create Or Update

Use bash, Azure CLI and REST API to access CosmosDB - how to get token and hash right?

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2