Azure API Management サービスからの外部サービスの使用Using external services from the Azure API Management service

Azure API Management サービスに含まれるポリシーでは、着信要求、送信応答、および基本的な構成情報のみを使用した有用なさまざまな処理を実行できます。The policies available in Azure API Management service can do a wide range of useful work based purely on the incoming request, the outgoing response, and basic configuration information. 一方、API Management ポリシーでは外部サービスと通信することもできるため、さらに可能性が広がります。However, being able to interact with external services from API Management policies opens up many more opportunities.

Azure Event Hub サービスでログ記録、監視、および分析を行う際の通信の方法については、既に学習しています。You have previously seen how to interact with the Azure Event Hub service for logging, monitoring, and analytics. この記事では、外部の任意の HTTP ベースのサービスと通信するポリシーのデモを行います。This article demonstrates policies that allow you to interact with any external HTTP-based service. こうしたポリシーは、リモート イベントをトリガーしたり、元の要求と応答を何らかの方法で操作する情報を取得したりする場合に使用できます。These policies can be used for triggering remote events or for retrieving information that is used to manipulate the original request and response in some way.

Send-One-Way-RequestSend-One-Way-Request

外部サービスに何らかの種類の重要なイベントを通知する、外部通信の最も単純な要求スタイルは、おそらくファイア アンド フォーゲットでしょう。Possibly the simplest external interaction is the fire-and-forget style of request that allows an external service to be notified of some kind of important event. 制御フロー ポリシー choose は、関心のある任意の種類の状態検出に使用できます。The control flow policy choose can be used to detect any kind of condition that you are interested in. 条件が満たされると、send-one-way-request ポリシーを使用して外部 HTTP 要求を行うことができます。If the condition is satisfied, you can make an external HTTP request using the send-one-way-request policy. これは、Hipchat、Slack などのメッセージング システム、SendGrid または MailChimp のようなメール API、または PagerDuty などの重要なサポート インシデントへの要求である場合があります。This could be a request to a messaging system like Hipchat or Slack, or a mail API like SendGrid or MailChimp, or for critical support incidents something like PagerDuty. こうしたメッセージング システムすべてにシンプルな HTTP API があり、呼び出すことができます。All of these messaging systems have simple HTTP APIs that can be invoked.

Slack を使用した警告Alerting with Slack

以下の例では、HTTP 応答のステータス コードが 500 以上の場合に、Slack チャット ルームにメッセージを送信する方法を示します。The following example demonstrates how to send a message to a Slack chat room if the HTTP response status code is greater than or equal to 500. 500 の範囲エラーは、API のクライアントが自動的に解決できない、バックエンド API に問題があることを示します。A 500 range error indicates a problem with the backend API that the client of the API cannot resolve themselves. 通常、これは API Management 側で何らかの介入が必要です。It usually requires some kind of intervention on API Management part.

<choose>
    <when condition="@(context.Response.StatusCode >= 500)">
      <send-one-way-request mode="new">
        <set-url>https://hooks.slack.com/services/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</set-url>
        <set-method>POST</set-method>
        <set-body>@{
                return new JObject(
                        new JProperty("username","APIM Alert"),
                        new JProperty("icon_emoji", ":ghost:"),
                        new JProperty("text", String.Format("{0} {1}\nHost: {2}\n{3} {4}\n User: {5}",
                                                context.Request.Method,
                                                context.Request.Url.Path + context.Request.Url.QueryString,
                                                context.Request.Url.Host,
                                                context.Response.StatusCode,
                                                context.Response.StatusReason,
                                                context.User.Email
                                                ))
                        ).ToString();
            }</set-body>
      </send-one-way-request>
    </when>
</choose>

Slack には、着信 Web フックの概念があります。Slack has the notion of inbound web hooks. 着信 Web フックを構成するとき、Slack は、単純な POST 要求を行い Slack チャネルにメッセージを渡す、特殊な URL を生成します。When configuring an inbound web hook, Slack generates a special URL, which allows you to do a simple POST request and to pass a message into the Slack channel. 作成する JSON 本文は、Slack によって定義されている形式に基づきます。The JSON body that you create is based on a format defined by Slack.

Slack Web フック

ファイア アンド フォーゲットは十分かIs fire and forget good enough?

ファイア アンド フォーゲット スタイルの要求には、あるトレードオフがあります。There are certain tradeoffs when using a fire-and-forget style of request. 何らかの理由で要求が失敗しても、エラーは報告されません。If for some reason, the request fails, then the failure is not be reported. この特定の状況に対し、複雑な 2 次的なエラー レポート システムや、応答の待機に関わる追加のパフォーマンス コストに対する保証はありません。In this particular situation, the complexity of having a secondary failure reporting system and the additional performance cost of waiting for the response is not warranted. 応答の確認が重要なシナリオの場合、より適切なオプションは send-request ポリシーです。For scenarios where it is essential to check the response, then the send-request policy is a better option.

send-requestSend-Request

send-request ポリシーは、外部サービスを使用し複雑な処理機能を実行したり、さらにポリシーを処理したりするために API Management サービスにデータを返すことに使用できます。The send-request policy enables using an external service to perform complex processing functions and return data to the API management service that can be used for further policy processing.

参照トークンの承認Authorizing reference tokens

API Management の主な機能には、バックエンド リソースの保護があります。A major function of API Management is protecting backend resources. API によって使用される承認サーバーで、Azure Active Directory のように、その OAuth2 フローの一部として JWT トークンが作成される場合、validate-jwt ポリシーを使用すると、そのトークンの有効性を検証できます。If the authorization server used by your API creates JWT tokens as part of its OAuth2 flow, as Azure Active Directory does, then you can use the validate-jwt policy to verify the validity of the token. 一部の承認サーバーでは、承認サーバーへのコールバックを行わなければ検証できない参照トークンと呼ばれるものが作成されます。Some authorization servers create what are called reference tokens that cannot be verified without making a callback to the authorization server.

標準化されたイントロスペクションStandardized introspection

これまで、承認サーバーで参照トークンを検証するための標準的な方法はありませんでした。In the past, there has been no standardized way of verifying a reference token with an authorization server. ただし、最近 IETF から、トークンの有効性をリソース サーバーが検証する方法を定義した標準 RFC 7662 の提案が公開されました。However a recently proposed standard RFC 7662 was published by the IETF that defines how a resource server can verify the validity of a token.

トークンの抽出Extracting the token

まず、トークンを Authorization ヘッダーから抽出します。The first step is to extract the token from the Authorization header. ヘッダー値は、RFC 6750 に従って、Bearer 承認スキーム、単一スペース、および承認トークンを使用して形式を指定する必要があります。The header value should be formatted with the Bearer authorization scheme, a single space, and then the authorization token as per RFC 6750. 残念ながら、認証スキームが省略されている場合もあります。Unfortunately there are cases where the authorization scheme is omitted. これに解析時に対応するため、API Management は、スペースでヘッダー値を分割し、返された文字列の配列から最後の文字を選択します。To account for this when parsing, API Management splits the header value on a space and selects the last string from the returned array of strings. これにより、不適切な形式の承認ヘッダーに対応できます。This provides a workaround for badly formatted authorization headers.

<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />

検証要求の実行Making the validation request

承認トークンを取得した API Management は、トークンの検証要求を実行できます。Once API Management has the authorization token, API Management can make the request to validate the token. RFC 7662 では、この処理をイントロスペクションと呼び、イントロスペクション リソースに HTML フォームを POST することを求めています。RFC 7662 calls this process introspection and requires that you POST an HTML form to the introspection resource. HTML フォームには、キー tokenと共にキーと値のペアが少なくとも 1 つ含まれている必要があります。The HTML form must at least contain a key/value pair with the key token. 悪意のあるクライアントが有効なトークンを探さないよう、承認サーバーへのこの要求も認証される必要があります。This request to the authorization server must also be authenticated, to ensure that malicious clients cannot go trawling for valid tokens.

<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
  <set-url>https://microsoft-apiappec990ad4c76641c6aea22f566efc5a4e.azurewebsites.net/introspection</set-url>
  <set-method>POST</set-method>
  <set-header name="Authorization" exists-action="override">
    <value>basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>
  </set-header>
  <set-header name="Content-Type" exists-action="override">
    <value>application/x-www-form-urlencoded</value>
  </set-header>
  <set-body>@($"token={(string)context.Variables["token"]}")</set-body>
</send-request>

応答の確認Checking the response

response-variable-name 属性は、返された応答へのアクセスを提供するために使用されます。The response-variable-name attribute is used to give access the returned response. このプロパティで定義されている名前は、IResponse オブジェクトにアクセスする context.Variables ディクショナリへのキーとして使用できます。The name defined in this property can be used as a key into the context.Variables dictionary to access the IResponse object.

応答オブジェクトから本文を取得できます。RFC 7622 は、応答は JSON オブジェクトである必要があり、ブール値である active というプロパティが少なくとも 1 つ必要であることを API Management に示します。From the response object, you can retrieve the body and RFC 7622 tells API Management that the response must be a JSON object and must contain at least a property called active that is a boolean value. active が true の場合、トークンは有効であるとみなされます。When active is true then the token is considered valid.

レポートのエラーReporting failure

<choose> ポリシーを使用すると、トークンの有効性を検出できます。無効である場合は、401 応答が返されます。You can use a <choose> policy to detect if the token is invalid and if so, return a 401 response.

<choose>
  <when condition="@((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
    <return-response response-variable-name="existing response variable">
      <set-status code="401" reason="Unauthorized" />
      <set-header name="WWW-Authenticate" exists-action="override">
        <value>Bearer error="invalid_token"</value>
      </set-header>
    </return-response>
  </when>
</choose>

bearer トークンの使用方法が記載されている RFC 6750 に従い、API Management は、401 応答と共に WWW-Authenticate ヘッダーも返します。As per RFC 6750 which describes how bearer tokens should be used, API Management also returns a WWW-Authenticate header with the 401 response. WWW-Authenticate は、正しく認証される要求を構築する方法をクライアントに指示することを目的としています。The WWW-Authenticate is intended to instruct a client on how to construct a properly authorized request. OAuth2 フレームワークで実行可能なアプローチは多岐にわたるため、必要なすべての情報を通信することは困難です。Due to the wide variety of approaches possible with the OAuth2 framework, it is difficult to communicate all the needed information. さいわいにも、 クライアントが正しくリソース サーバーに要求を承認させる方法を支援する取り組みが進行中です。Fortunately there are efforts underway to help clients discover how to properly authorize requests to a resource server.

最終的なソリューションFinal solution

これらをすべてまとめると、次のポリシーがあることになります。At the end, you get the following policy:

<inbound>
  <!-- Extract Token from Authorization header parameter -->
  <set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />

  <!-- Send request to Token Server to validate token (see RFC 7662) -->
  <send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
    <set-url>https://microsoft-apiappec990ad4c76641c6aea22f566efc5a4e.azurewebsites.net/introspection</set-url>
    <set-method>POST</set-method>
    <set-header name="Authorization" exists-action="override">
      <value>basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>
    </set-header>
    <set-header name="Content-Type" exists-action="override">
      <value>application/x-www-form-urlencoded</value>
    </set-header>
    <set-body>@($"token={(string)context.Variables["token"]}")</set-body>
  </send-request>

  <choose>
          <!-- Check active property in response -->
          <when condition="@((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
              <!-- Return 401 Unauthorized with http-problem payload -->
              <return-response response-variable-name="existing response variable">
                  <set-status code="401" reason="Unauthorized" />
                  <set-header name="WWW-Authenticate" exists-action="override">
                      <value>Bearer error="invalid_token"</value>
                  </set-header>
              </return-response>
          </when>
      </choose>
  <base />
</inbound>

これは、有用な外部サービスを API Management サービスを経由する要求および応答の処理に統合する send-request ポリシーを使用する数多い例の中のほんの 1 つです。This is only one of many examples of how the send-request policy can be used to integrate useful external services into the process of requests and responses flowing through the API Management service.

応答の構成Response Composition

send-request ポリシーを使用すると、前述の例のようにバックエンド システムにプライマリ要求を拡張できるほか、バックエンド呼び出しに完全に置き換わるものとして使用することができます。The send-request policy can be used for enhancing a primary request to a backend system, as you saw in the previous example, or it can be used as a complete replace for of the backend call. このテクニックを使用すると、複数の異なるシステムからまとめた複合リソースを簡単に作成できます。Using this technique you can easily create composite resources that are aggregated from multiple different systems.

ダッシュボードの構築Building a dashboard

ダッシュボードを動かすためなど、複数のバックエンド システムに存在する情報を公開したい場合があります。Sometimes you want to be able to expose information that exists in multiple backend systems, for example, to drive a dashboard. KPI はすべて異なるバックエンドから得られますが、それらに直接アクセスできないようにし、1 つの要求で、すべての情報を取得できるようになると都合がよい場合があります。The KPIs come from all different back-ends, but you would prefer not to provide direct access to them and it would be nice if all the information could be retrieved in a single request. 一部のバックエンド情報は、細分化したり、好ましくない部分は削除したりする必要があるでしょう。Perhaps some of the backend information needs some slicing and dicing and a little sanitizing first! その複合リソースをキャッシュできると、パフォーマンスが悪いメトリックの変化を確認するためにユーザーはよく F5 キーを打つため、バックエンドに対する負荷を軽減できるでしょう。Being able to cache that composite resource would be a useful to reduce the backend load as you know users have a habit of hammering the F5 key in order to see if their underperforming metrics might change.

リソースのフェイクFaking the resource

ダッシュボード リソースを構築するには、まず Azure Portal で新しい操作を構成します。The first step to building the dashboard resource is to configure a new operation in the Azure portal. これは、動的なリソースを構築する複合ポリシーを構成するプレースホルダー操作です。This is a placeholder operation used to configure a composition policy to build the dynamic resource.

ダッシュボード操作

要求の作成Making the requests

操作を作成したら、その操作に特化したポリシーを構成できます。Once the operation has been created, you can configure a policy specifically for that operation.

ダッシュボード操作

このためには、バックエンドに送信できるよう、まず受信要求のすべてのクエリ パラメーターを抽出します。The first step is to extract any query parameters from the incoming request, so that you can forward them to the backend. この例では、ダッシュボードには、期間に基づいて情報が表示されているので、fromDatetoDate のパラメーターがあります。In this example, the dashboard is showing information based on a period of time and therefore has a fromDate and toDate parameter. 要求 URL から情報を抽出するには、set-variable ポリシーを使用します。You can use the set-variable policy to extract the information from the request URL.

<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">

この情報を入手できたら、バックエンド システムすべてに要求を実行できます。Once you have this information, you can make requests to all the backend systems. 各要求は、パラメーター情報を使用して新しい URL を構築し、それぞれのサーバーを呼び出し、コンテキスト変数に応答を格納します。Each request constructs a new URL with the parameter information and calls its respective server and stores the response in a context variable.

<send-request mode="new" response-variable-name="revenuedata" timeout="20" ignore-error="true">
  <set-url>@($"https://accounting.acme.com/salesdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
  <set-method>GET</set-method>
</send-request>

<send-request mode="new" response-variable-name="materialdata" timeout="20" ignore-error="true">
  <set-url>@($"https://inventory.acme.com/materiallevels?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
  <set-method>GET</set-method>
</send-request>

<send-request mode="new" response-variable-name="throughputdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
  <set-method>GET</set-method>
</send-request>

<send-request mode="new" response-variable-name="accidentdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/accidentdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
  <set-method>GET</set-method>
</send-request>

これらの要求は、不適切にも連続して実行されます。These requests execute in sequence, which is not ideal.

応答Responding

複合応答を構築するには、return-response ポリシーを使用します。To construct the composite response, you can use the return-response policy. set-body 要素では式を使用して、すべてのコンポーネント表現がプロパティとして埋め込まれた、新しい JObject を構築できます。The set-body element can use an expression to construct a new JObject with all the component representations embedded as properties.

<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("revenuedata",((IResponse)context.Variables["revenuedata"]).Body.As<JObject>()),
                  new JProperty("materialdata",((IResponse)context.Variables["materialdata"]).Body.As<JObject>()),
                  new JProperty("throughputdata",((IResponse)context.Variables["throughputdata"]).Body.As<JObject>()),
                  new JProperty("accidentdata",((IResponse)context.Variables["accidentdata"]).Body.As<JObject>())
                  ).ToString())
  </set-body>
</return-response>

完全なポリシーは以下のようになります。The complete policy looks as follows:

<policies>
    <inbound>

  <set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
  <set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">

    <send-request mode="new" response-variable-name="revenuedata" timeout="20" ignore-error="true">
      <set-url>@($"https://accounting.acme.com/salesdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
      <set-method>GET</set-method>
    </send-request>

    <send-request mode="new" response-variable-name="materialdata" timeout="20" ignore-error="true">
      <set-url>@($"https://inventory.acme.com/materiallevels?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
      <set-method>GET</set-method>
    </send-request>

    <send-request mode="new" response-variable-name="throughputdata" timeout="20" ignore-error="true">
    <set-url>@($"https://production.acme.com/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
      <set-method>GET</set-method>
    </send-request>

    <send-request mode="new" response-variable-name="accidentdata" timeout="20" ignore-error="true">
    <set-url>@($"https://production.acme.com/accidentdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
      <set-method>GET</set-method>
    </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("revenuedata",((IResponse)context.Variables["revenuedata"]).Body.As<JObject>()),
                      new JProperty("materialdata",((IResponse)context.Variables["materialdata"]).Body.As<JObject>()),
                      new JProperty("throughputdata",((IResponse)context.Variables["throughputdata"]).Body.As<JObject>()),
                      new JProperty("accidentdata",((IResponse)context.Variables["accidentdata"]).Body.As<JObject>())
                      ).ToString())
      </set-body>
    </return-response>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
</policies>

プレースホルダー操作の構成で、ダッシュボード リソースが少なくとも 1 時間キャッシュされるように構成できます。In the configuration of the placeholder operation, you can configure the dashboard resource to be cached for at least an hour.

まとめSummary

Azure API Management サービスには、HTTP トラフィックに選択的に適用できる、バックエンド サービスの構成に使用できる、柔軟なポリシーがあります。Azure API Management service provides flexible policies that can be selectively applied to HTTP traffic and enables composition of backend services. 警告機能、確認、検証機能で API ゲートウェイを拡張したい場合、または複数のバックエンド サービスを使用し新しい複合リソースを作成したい場合のために、 send-request と関連ポリシーはさまざまな可能性を開きます。Whether you want to enhance your API gateway with alerting functions, verification, validation capabilities or create new composite resources based on multiple backend services, the send-request and related policies open a world of possibilities.