このエントリは2023/12/10現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。
問い合わせ
昨日の質問をして来た人からの更問(更更問、とも言える)。
Azure API Management (以下、APIM) のサブスクリプションキーはQuery ParameterもしくはHTTP Headerを使うことになっているが、Request本文に指定した値を使うことはできないか?
仕様上はQuery ParameterもしくはRequest Headerに設定しなければならず、端的に答えると「そんなことはできない」でおしまいなのだが、多段構成であれば実現可能な場合がある。
ユースケース
例えば、Request本文に
{
"subscription-key":"xxxxxx"
}
というメッセージが入っていたらAPIのアクセスを許可する、という具合。
方法
API ManagementでホストするAPIを2段(API-1、API-2)にする。API-1をフロントエンドとし、通常のAPI利用者はAPI-1を呼び出す。処理の実体はAPI-2で実施する。いずれも同一のAPIMインスタンスにホストできるので、2段にするからといって、APIMインスタンスを複数用意する必要はない。
| APIについて | サブスクリプション | バックエンドサービス | |
|---|---|---|---|
| API-1 | Request本文に含まれるsubscription-keyをチェック。 含まれていれば、バックエンドサービスとして設定する後段のAPIに対して、Header(もしくはQuery Parameter)でサブスクリプションキーを渡すよう構成する。 含まれていない場合は403を返す。 | 構成しない | API-2 |
| API-2 | 本来の処理を提供するAPI | 構成する | N/A |
図示すると以下のようなイメージ。

実装
前段のAPI (API-1) では、以下のようなポリシーを構成しておく。
- InboundポリシーでキーがRequest本文に含まれているかを確認
- あれば値を取得してHTTP Headerに指定する
なお、API-1へのRequest本文はこの後使わないので、2回目のcontext.Request.Body.As<T>の呼び出しではpreserveContent=trueを指定していない。
以下のポリシーは厳密なエラー処理(JSON Schemaに対するValidationなど)は含めていないので、本運用時には適切なロジックが必要。
<policies>
<inbound>
<base />
<set-variable name="bExist" value="@(context.Request.Body.As<JObject>(preserveContent: true).ContainsKey("subscription-key"))" />
<choose>
<when condition="@((bool)context.Variables["bExist"])">
<set-header name="Ocp-Apim-Subscription-Key" exists-action="override">
<value>@{
JObject inBody = context.Request.Body.As<JObject>();
return inBody["subscription-key"].ToString();
}</value>
</set-header>
<set-method>GET</set-method>
</when>
<otherwise>
<return-response>
<set-status code="403" />
<set-header name="content-type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("error", "No subscription key is found.")
).ToString();
}</set-body>
</return-response>
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
注意
多段にするとAPIの障害分析時に見通しが悪くなる可能性があるので注意が必要。また、内部モードでAPIMをデプロイしていて、HTTP 500 BackendConnectionFailure エラーが返る場合には、以下のブログエントリにあるような対応が必要になる場合がある。
Self-Chained APIM request limitation in internal Virtual network mode (Developer and Premium tier)
https://techcommunity.microsoft.com/t5/azure-paas-blog/self-chained-apim-request-limitation-in-internal-virtual-network/ba-p/1940417
この制約は内部ロードバランサー配下にあるノードから当該内部ロードバランサーを呼び出すことによるものと同様なので、プロキシを挟む(Application Gatewayを挟んでもいい)ことで回避できる。
参加しているロード バランサー バックエンド プール VM から内部 ロード バランサー フロントエンドにアクセスしている / Access of the internal load balancer frontend from the participating load balancer backend pool VM
https://learn.microsoft.com/azure/load-balancer/load-balancer-troubleshoot-backend-traffic#cause-4-access-of-the-internal-load-balancer-frontend-from-the-participating-load-balancer-backend-pool-vm