Teilen über


Verwenden externer Dienste über den Azure API Management-Dienst

GILT FÜR: Alle API Management-Ebenen

Mit den im Azure API Management-Dienst verfügbaren Richtlinien können Sie zahlreiche nützliche Aufgaben durchführen, ausschließlich basierend auf der eingehenden Anforderung, der ausgehenden Antwort und den grundlegenden Konfigurationsinformationen. Die Interaktionsfähigkeit mit externen Diensten mithilfe von API Management-Richtlinien eröffnet jedoch viele weitere Möglichkeiten.

In früheren Artikeln haben Sie gesehen, wie Sie mit dem Azure Event Hubs-Dienst zur Protokollierung, Überwachung und Analyse interagieren. In diesem Artikel werden Richtlinien gezeigt, die eine Interaktion mit beliebigen externen HTTP-basierten Diensten ermöglichen. Diese Richtlinien können zum Auslösen von Remoteereignissen oder zum Abrufen von Informationen verwendet werden, die wiederum zum Verändern der ursprünglichen Anforderung und Antwort genutzt werden.

Send-One-Way-Request

Die einfachste externe Interaktion ist möglicherweise eine Anforderung im fire-and-forget-Stil (Auslösen und Vergessen), mit der ein externer Dienst über ein wichtiges Ereignis jeglicher Art benachrichtigt werden kann. Die Ablaufsteuerungsrichtlinie choose kann verwendet werden, um jede Art von Bedingung zu erkennen, an der Sie interessiert sind. Wenn die Bedingung erfüllt ist, können Sie mithilfe der Richtlinie send-one-way-request eine externe HTTP-Anforderung ausführen. Diese Anforderung kann an ein Messagingsystem wie Hipchat oder Slack oder eine Mail-API wie SendGrid oder MailChimp oder für kritische Supportvorfälle wie PagerDuty sein. Alle diese Messagingsysteme verfügen über einfache HTTP-APIs, die aufgerufen werden können.

Benachrichtigung mit Slack

Im folgenden Beispiel wird gezeigt, wie eine Nachricht an einen Slack-Chatraum gesendet wird, wenn der Statuscode der HTTP-Antwort größer als oder gleich 500 ist. Ein 500-Bereichsfehler zeigt ein Problem mit der Back-End-API an, das der Client der API nicht selbst beheben kann. Es erfordert in der Regel eine Art von Intervention im Teil der API-Verwaltung.It usually requires some kind of intervention on the part of 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>

Bei Slack kommen eingehende Webhooks zum Einsatz. Wenn ein eingehender Web-Hook konfiguriert wird, generiert Slack eine spezielle URL, mit der Sie eine einfache POST-Anforderung ausführen und eine Nachricht an den Slack-Kanal übergeben können. Der erstellte JSON-Textkörper basiert auf einem von Slack definierten Format.

Screenshot eines Slack-Web-Hooks.

Reicht „Fire and Forget“ aus?

Es gibt einige Nachteile bei einer Anforderung im Fire-and-Forget-Stil. Wenn die Anforderung aus irgendeinem Grund fehlschlägt, wird der Fehler nicht gemeldet. In dieser Situation ist die Komplexität eines sekundären Fehlerberichterstattungssystems und die zusätzlichen Leistungskosten für das Warten auf die Antwort nicht gerechtfertigt. Für Szenarien, in denen es wichtig ist, die Antwort zu überprüfen, ist die Send-Request-Richtlinie eine bessere Option.

send-request

Die send-request -Richtlinie ermöglicht die Nutzung eines externen Diensts zum Durchführen komplexer Verarbeitungsfunktionen und zum Zurückgeben von Daten an den API Management-Dienst, der für die weitere Richtlinienverarbeitung verwendet werden kann.

Autorisieren von Verweistoken

Eine wichtige Funktion von API Management ist der Schutz der Back-End-Ressourcen. Wenn der von Ihrer API verwendete Autorisierungsserver JSON-Webtoken (JWTs) als Teil seines OAuth2-Flusses erstellt, wie Microsoft Entra ID es tut, können Sie die validate-jwt-Richtlinie oder validate-azure-ad-token-Richtlinie verwenden, um die Gültigkeit des Tokens zu überprüfen. Einige Autorisierungsserver erstellen, was als Referenztoken bezeichnet wird, die nicht überprüft werden können, ohne einen Rückruf an den Autorisierungsserver vorzunehmen.

Standardisierte Introspection

In der Vergangenheit gab es keine standardisierte Möglichkeit, ein Referenztoken mit einem Autorisierungsserver zu überprüfen. Die Internet Engineering Task Force (IETF) hat jedoch kürzlich den vorgeschlagenen Standard RFC 7662 veröffentlicht, der definiert, wie ein Ressourcenserver die Gültigkeit eines Tokens überprüfen kann.

Extrahieren des Tokens

Der erste Schritt besteht im Extrahieren des Tokens aus dem Autorisierungsheader. Der Headerwert muss mit dem Bearer-Autorisierungsschema, einem einzelnen Leerzeichen, gefolgt vom Autorisierungstoken gemäß RFC 6750 formatiert werden. Es gibt leider Fälle, in denen das Autorisierungsschema weggelassen wird. Um dieser Unterlassung bei der Analyse Rechnung zu tragen, teilt API Management den Headerwert mit einem Leerzeichen und wählt die letzte Zeichenfolge aus dem zurückgegebenen Array von Zeichenfolgen aus. Diese Methode bietet eine Problemumgehung für falsch formatierte Autorisierungsheader.

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

Ausführen der Überprüfungsanforderung

Nachdem das Autorisierungstoken von API Management empfangen wurde, kann die Anforderung zum Überprüfen des Tokens ausgeführt werden. In RFC 7662 wird dieser Prozess „Introspection“ genannt. Dazu muss mittels POST ein HTML-Formular in der Introspection-Ressource veröffentlicht werden. Das HTML-Formular muss mindestens ein Schlüssel/Wert-Paar mit dem Schlüssel token enthalten. Diese Anforderung an den Autorisierungsserver muss ebenfalls authentifiziert werden, um sicherzustellen, dass böswillige Clients keine Trawlung für gültige Token ausführen können.

<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>

Überprüfen der Antwort

Das response-variable-name Attribut wird verwendet, um Zugriff auf die zurückgegebene Antwort zu gewähren. Der in dieser Eigenschaft definierte Name kann als Schlüssel für das context.Variables-Wörterbuch verwendet werden, um auf das IResponse-Objekt zuzugreifen.

Aus dem Antwortobjekt können Sie den Textkörper abrufen. RFC 7622 weist API Management an, dass die Antwort ein JSON-Objekt sein und mindestens eine Eigenschaft namens active enthalten muss, bei der es sich um einen booleschen Wert handelt. Ist active "true", wird das Token als gültig betrachtet.

Alternativ, wenn der Autorisierungsserver das "active"-Feld nicht enthält, um anzugeben, ob das Token gültig ist, verwenden Sie ein HTTP-Client-Tool wie curl, um zu bestimmen, welche Eigenschaften in einem gültigen Token festgelegt sind. Wenn beispielsweise eine gültige Tokenantwort eine Eigenschaft mit dem Namen "expires_in"enthält, überprüfen Sie, ob dieser Eigenschaftsname in der Autorisierungsserverantwort auf diese Weise vorhanden ist:

<when condition="@(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>().Property("expires_in") == null)">

Melden eines Fehlers

Sie können mithilfe einer <choose>-Richtlinie ermitteln, ob das Token ungültig ist. Wenn dies der Fall ist, wird eine 401-Antwort zurückgegeben.

<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>

Gemäß RFC 6750, der beschreibt, wie bearer Token verwendet werden sollen, gibt die API-Verwaltung auch einen WWW-Authenticate Header mit der 401-Antwort zurück. WWW-Authenticate soll einen Client anweisen, wie eine ordnungsgemäß autorisierte Anforderung erstellt wird. Aufgrund der vielzahl von Ansätzen, die mit dem OAuth2-Framework möglich sind, ist es schwierig, alle erforderlichen Informationen zu kommunizieren. Glücklicherweise gibt es derzeit Bemühungen, die Clients dabei helfen sollen, herauszufinden, wie Anforderungen an einen Ressourcenserver ordnungsgemäß autorisiert werden.

Endgültige Lösung

Schließlich erhalten Sie die folgende Richtlinie:

<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>

Dieses Beispiel ist nur eine von vielen, die veranschaulichen, wie die send-request Richtlinie verwendet werden kann, um nützliche externe Dienste in den Prozess der Anforderungen und Antworten zu integrieren, die über den API-Verwaltungsdienst fließen.

Antwortkomposition

Die send-request Richtlinie kann verwendet werden, um eine primäre Anforderung an ein Back-End-System zu verbessern, wie Sie im vorherigen Beispiel gesehen haben, oder sie kann verwendet werden, um den Back-End-Aufruf vollständig zu ersetzen. Mithilfe dieser Technik können Sie mühelos zusammengesetzte Ressourcen erstellen, die aus verschiedenen Systemen aggregiert werden.

Erstellen eines Dashboards

Gelegentlich möchten Sie in der Lage sein, Informationen aus mehreren Back-End-Systemen verfügbar zu machen, um beispielsweise ein Dashboard zu steuern. Die Key Performance Indicators (KPIs) stammen aus verschiedenen Backends, aber Sie möchten keinen direkten Zugriff darauf gewähren. Dennoch wäre es schön, wenn alle Informationen in einer einzigen Anforderung abgerufen werden könnten. Einige der Back-End-Informationen müssen womöglich aufgegliedert und zunächst etwas bereinigt werden! Dass man diese zusammengesetzte Ressource zwischenspeichern kann, wäre eine nützliche Möglichkeit, um die Back-End-Auslastung zu reduzieren, da Sie wissen, dass Benutzer häufig die F5-Taste drücken, um festzustellen, ob sich ihre schlecht abschneidenden Metriken ändern könnten.

Falsche Ressource

Der erste Schritt beim Erstellen der Dashboardressource ist die Konfiguration eines neuen Vorgangs im Azure-Portal. Dieser Platzhaltervorgang wird verwendet, um eine Kompositionsrichtlinie zum Erstellen der dynamischen Ressource zu konfigurieren.

Screenshot eines neuen Dashboardvorgangs, der im Azure-Portal konfiguriert wird.

Durchführen der Anforderungen

Nachdem der Vorgang erstellt wurde, können Sie eine Richtlinie speziell für diesen Vorgang konfigurieren.

Screenshot des Bildschirms

Im ersten Schritt werden Abfrageparameter aus der eingehenden Anforderung extrahiert, damit sie an das Back-End weitergeleitet werden können. In diesem Beispiel zeigt das Dashboard Informationen basierend auf einem Zeitraum und umfasst daher einen fromDate- und einen toDate-Parameter. Sie können die set-variable-Richtlinie verwenden, um die Informationen aus der Anforderungs-URL zu extrahieren.

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

Sobald diese Informationen vorliegen, können Sie Anforderungen an alle Back-End-Systeme senden. Jede Anforderung erstellt eine neue URL mit den Parameterinformationen, ruft den entsprechenden Server auf und speichert die Antwort in einer Kontextvariablen.

<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>

Die API-Verwaltung sendet diese Anforderungen sequenziell.

Antworten

Um die zusammengesetzte Antwort zu generieren, kann die Richtlinie return-response verwendet werden. Das set-body-Element kann einen Ausdruck verwenden, um ein neues JObject mit allen Komponentendarstellungen zu erstellen, die als Eigenschaften eingebettet sind.

<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>

Die vollständige Richtlinie sieht folgendermaßen aus:

<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>

Zusammenfassung

Der Azure API Management-Dienst bietet flexible Richtlinien, die selektiv auf den HTTP-Verkehr angewendet werden können und die Zusammensetzung der Back-End-Dienste ermöglichen. Unabhängig davon, ob Sie das API-Gateway mit Alarmfunktionen, Überprüfungs- und Validierungsfunktionen verbessern möchten oder ob Sie neue zusammengesetzte Ressourcen basierend auf mehreren Back-End-Diensten erstellen möchten, bieten die send-request -Richtlinie und verwandte Richtlinien vollkommen neue Möglichkeiten.