はじめに
Azure API Managementのポリシー内では、独自のAPIを呼び出すことが可能です。
そのため、API Managementのポリシーだけではまかなえない、様々なカスタム処理をアプリへのアクセス前にさせることができます。
今回は、前回の記事で作成したポリシーを拡張し、「携帯端末でPC向けアプリにアクセスした際にモバイル向けアプリにリダイレクトさせる」といった例で、
API Managementで独自APIを呼び出し、その結果に応じてリダイレクトさせる方法ご紹介します。
今回ご紹介する例を図にするとこのようになります。
前提
- 前回の記事同様、下記を前提としています。
独自APIの内容
API Managementで独自APIを呼び出し方の前に、まずは呼び出し先となる独自APIのリクエスト/レスポンスとサンプルコードをご紹介します。
リクエスト例
Request Bodyの内容は下記の通りです。API Managementにアクセスした端末のUserAgentを受け取ります。
{
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1"
}
レスポンス例
レスポンスでは、受け取ったUserAgentをもとにリダイレクトをするかどうかの結果を返します。
{
"isRedirect": true
}
サンプルコード
前述のリクエスト内容で受け取ったUserAgentから、モバイル端末(iPhone, Android)かどうかを判定し、モバイルの場合はリダイレクトするよう結果を返します。
下記はAzure FunctionのHTTP Triggerを利用した場合のサンプルコードです。
Azure Functionのランタイムバージョンはv4、フレームワークは.NET6としています。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.IO;
using System.Threading.Tasks;
namespace ApiManagementFunction
{
public static class ApiManagementFunction
{
/// <summary>
/// UserAgentからリダイレクトさせるかを判定する
/// </summary>
/// <param name="req"></param>
/// <param name="log"></param>
/// <returns></returns>
[FunctionName("IsRedirect")]
public static async Task<ActionResult<IsRedirectResponseViewModel>> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
var userAgent = ((string)(data?.userAgent)).ToLower();
var vm = new IsRedirectResponseViewModel
{
IsRedirect = false
};
// UserAgentからモバイル端末かどうかを判定する
if (userAgent.IndexOf("iphone") > -1 || (userAgent.IndexOf("android") > -1 && userAgent.IndexOf("mobile") > -1))
{
// モバイル端末であれば、リダイレクトさせる
vm.IsRedirect = true;
}
log.LogInformation($"User Agent = {userAgent}, IsRedirect = {vm.IsRedirect}");
return new OkObjectResult(vm);
}
}
/// <summary>
/// APIレスポンス用ViewModel
/// </summary>
public class IsRedirectResponseViewModel
{
/// <summary>
/// リダイレクトするかどうか
/// </summary>
public bool IsRedirect { get; set; }
}
}
API Managementポリシー設定
PCアプリ側のGETオペレーションのInbound processingポリシーに下記の要素を追加します。
Azure PortalのUIではこちらを選択するとポリシーの編集画面になります。
<policies>
<inbound>
<base />
<!-- 追加 -->
<!-- 1. 独自APIの呼び出し -->
<send-request mode="new" response-variable-name="response">
<!-- API URL -->
<set-url>https://{your app}.azurewebsites.net/api/IsRedirect</set-url>
<set-method>POST</set-method>
<!-- API Request Header -->
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="x-functions-key" exists-action="override">
<value>{your function key}</value>
</set-header>
<!-- API Request Body -->
<set-body>@{
string userAgent = context.Request.Headers.GetValueOrDefault("User-Agent","");
return new JObject(
new JProperty("userAgent", userAgent)
).ToString();
}</set-body>
</send-request>
<!-- 2. APIのレスポンスをオブジェクト型の変数に定義 -->
<set-variable name="apiResult" value="@(((IResponse)context.Variables["response"]).Body.As<JObject>())" />
<!-- 3. APIの結果から判定 -->
<choose>
<when condition="@((bool)((JObject)context.Variables["apiResult"]).Property("isRedirect"))">
<!-- 4. リダイレクト -->
<return-response>
<set-status code="303" reason="Redirecting" />
<set-header name="Location" exists-action="override">
<value>@{
return "/v2";
}</value>
</set-header>
</return-response>
</when>
<otherwise />
</choose>
<!-- /追加 -->
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
解説
ポリシーの中でも、今回追加した箇所をピックアップします。
少しコードが複雑になっているので、それぞれ何をしているかの概要のみ説明します。詳細については各項目の参考ドキュメントをご参照ください。
<!-- 1. 独自APIの呼び出し -->
<send-request mode="new" response-variable-name="response">
<!-- API URL -->
<set-url>https://{your app}.azurewebsites.net/api/IsRedirect</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="x-functions-key" exists-action="override">
<value>{your function key}</value>
</set-header>
<!-- API Request Body -->
<set-body>@{
string userAgent = context.Request.Headers.GetValueOrDefault("User-Agent","");
return new JObject(
new JProperty("userAgent", userAgent)
).ToString();
}</set-body>
</send-request>
<!-- 2. APIのレスポンスをオブジェクト型の変数に定義 -->
<set-variable name="apiResult" value="@(((IResponse)context.Variables["response"]).Body.As<JObject>())" />
<!-- 3. APIの結果から判定 -->
<choose>
<when condition="@((bool)((JObject)context.Variables["apiResult"]).Property("isRedirect"))">
<!-- 4. リダイレクト -->
<return-response>
<set-status code="303" reason="Redirecting" />
<set-header name="Location" exists-action="override">
<value>@{
return "/v2";
}</value>
</set-header>
</return-response>
</when>
<otherwise />
</choose>
1. 独自APIの呼び出し
ポリシーのsend-request
の部分では、独自APIの定義を行います。
<!-- 1. 独自APIの呼び出し -->
<send-request mode="new" response-variable-name="response">
<!-- API URL -->
<set-url>https://{your app}.azurewebsites.net/api/IsRedirect</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<!-- API Key -->
<set-header name="x-functions-key" exists-action="override">
<value>{your function key}</value>
</set-header>
<!-- API Request Body -->
<set-body>@{
string userAgent = context.Request.Headers.GetValueOrDefault("User-Agent","");
return new JObject(
new JProperty("userAgent", userAgent)
).ToString();
}</set-body>
</send-request>
主な定義内容は下記の通りです。
-
set-url
:独自APIのURL -
set-method
:独自APIを呼び出すHTTPメソッド(GET, POSTなど) -
set-header
:独自APIのリクエストヘッダー- 上記の例では、
Content-Type
およびx-functions-key
(Azure Functionsのキー)を設定しています
- 上記の例では、
-
set-body
:Request Body- 上記の例では、リクエストに含まれるUserAgentの情報をBodyに設定しています
参考:https://learn.microsoft.com/ja-jp/azure/api-management/send-request-policy
2. APIのレスポンスをオブジェクト型の変数に定義
ポリシーのset-variable
の部分では、変数を定義します。
<!-- 2. APIのレスポンスをオブジェクト型の変数に定義 -->
<set-variable name="apiResult" value="@(((IResponse)context.Variables["response"]).Body.As<JObject>())" />
カッコが多く少しわかりづらいですが、上記の例では受け取ったレスポンスをオブジェクト型のapiResult
という名前の変数に代入しています。
参考:https://learn.microsoft.com/ja-jp/azure/api-management/set-variable-policy
3. APIの結果から判定
ポリシーのchoose
の部分では、ある条件に一致した場合のみ処理を行うよう制御します。C#やJavaのif-else文のようなものです。
<!-- 3. APIの結果から判定 -->
<choose>
<when condition="@((bool)((JObject)context.Variables["apiResult"]).Property("isRedirect"))">
<!-- 条件に一致した場合の処理 -->
</when>
<otherwise>
<!-- 条件に一致しなかった場合の処理 -->
</otherwise>
</choose>
主な定義内容は下記の通りです。
-
when
:condition
に条件式を記載し、when
タグ内部ではその条件に一致した場合の処理を実行します。- 上記の例では、APIから受け取ったレスポンスの
isRedirect
がtrueの場合はwhen内部を実行しています。
- 上記の例では、APIから受け取ったレスポンスの
-
otherwise
:when
で定義した条件に一致しなかった場合の処理を実行します。
参考:https://learn.microsoft.com/ja-jp/azure/api-management/choose-policy
4. リダイレクト
return-response
では、API Managementからのレスポンスを定義します。
<!-- 4. リダイレクト -->
<return-response>
<set-status code="303" reason="Redirecting" />
<set-header name="Location" exists-action="override">
<value>@{
return "/v2";
}</value>
</set-header>
</return-response>
今回は、モバイル向けアプリのパスへのリダイレクト処理を行っています。
こちらについては、前回の記事をご参照ください。
動作確認
API Managementで変更を適用後、PC向けアプリへのURLにアクセスしてみましょう。
- URL例:
https://<API Managementサービス名>.azure-api.net/
まずPCでアクセスしてみると、URLはそのまま変更されずにPC向けアプリにアクセスしています。
次にモバイル端末でアクセスしてみると、モバイル向けURLにリダイレクトできていることが確認できます。(キャプチャはChrome開発者ツールのモバイルエミュレーター機能を利用したものになります。)
ブラウザの開発者ツールでネットワークログを確認すると、下記のログが確認できます。
前回の記事同様に、リダイレクトされていますね。
まとめ
今回はPCアプリ⇒モバイルアプリのAPIでのみリダイレクト対応しましたが、独自APIを改修することで、逆のパターンものリダイレクト処理もできます。
また今回は割愛しましたが、独自APIでDB等の他リソースと連携させることにより様々なカスタマイズもできます。