Externe services van de Azure API Management-service gebruiken

VAN TOEPASSING OP: Alle API Management-lagen

Het beleid dat beschikbaar is in de Azure API Management-service kan een breed scala aan nuttige werkzaamheden uitvoeren op basis van de binnenkomende aanvraag, het uitgaande antwoord en basisconfiguratiegegevens. Als u echter kunt communiceren met externe services vanuit API Management-beleid, worden er nog veel meer mogelijkheden geopend.

U hebt eerder gezien hoe u kunt communiceren met de Azure Event Hub-service voor logboekregistratie, bewaking en analyse. In dit artikel ziet u beleidsregels waarmee u kunt communiceren met een externe HTTP-service. Deze beleidsregels kunnen worden gebruikt voor het activeren van externe gebeurtenissen of voor het ophalen van informatie die wordt gebruikt om de oorspronkelijke aanvraag en reactie op een of andere manier te manipuleren.

Eenrichtingsaanvraag verzenden

Mogelijk is de eenvoudigste externe interactie de fire-and-forget-stijl van de aanvraag waarmee een externe service op de hoogte kan worden gesteld van een belangrijk evenement. Het controlestroombeleid choose kan worden gebruikt om elk soort voorwaarde te detecteren waarin u geïnteresseerd bent. Als aan de voorwaarde wordt voldaan, kunt u een externe HTTP-aanvraag indienen met behulp van het beleid voor verzenden naar één richting. Dit kan een aanvraag zijn voor een berichtensysteem, zoals Hipchat of Slack, of een mail-API zoals SendGrid of MailChimp, of voor kritieke ondersteuningsincidenten, zoals PagerDuty. Al deze berichtensystemen hebben eenvoudige HTTP-API's die kunnen worden aangeroepen.

Waarschuwingen met Slack

In het volgende voorbeeld ziet u hoe u een bericht verzendt naar een Slack-chatruimte als de HTTP-antwoordstatuscode groter is dan of gelijk is aan 500. Een bereikfout van 500 geeft een probleem aan met de back-end-API dat de client van de API zichzelf niet kan oplossen. Er is meestal een interventie vereist voor het API Management-onderdeel.

<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 heeft het idee van binnenkomende webhook. Bij het configureren van een binnenkomende webhook genereert Slack een speciale URL, waarmee u een eenvoudige POST-aanvraag kunt uitvoeren en een bericht kunt doorgeven aan het Slack-kanaal. De JSON-hoofdtekst die u maakt, is gebaseerd op een indeling die is gedefinieerd door Slack.

Slack Web Hook

Is vuur en vergeet goed genoeg?

Er zijn bepaalde compromissen bij het gebruik van een fire-and-forget-stijl van aanvraag. Als de aanvraag om een of andere reden mislukt, wordt de fout niet gerapporteerd. In deze specifieke situatie is de complexiteit van het hebben van een secundair systeem voor foutrapportage en de extra prestatiekosten voor het wachten op het antwoord niet gerechtvaardigd. Voor scenario's waarin het essentieel is om het antwoord te controleren, is het beleid voor verzendaanvragen een betere optie.

Verzenden-aanvraag

Met send-request het beleid kan het gebruik van een externe service complexe verwerkingsfuncties uitvoeren en gegevens retourneren naar de API Management-service die kan worden gebruikt voor verdere beleidsverwerking.

Referentietokens autoriseren

Een belangrijke functie van API Management is het beveiligen van back-endbronnen. Als de autorisatieserver die door uw API wordt gebruikt JWT-tokens maakt als onderdeel van de OAuth2-stroom, zoals microsoft Entra-id dat doet, kunt u het validate-jwt beleid of validate-azure-ad-token beleid gebruiken om de geldigheid van het token te controleren. Sommige autorisatieservers maken referentietokens die niet kunnen worden geverifieerd zonder een callback naar de autorisatieserver te maken.

Gestandaardiseerde introspectie

In het verleden is er geen gestandaardiseerde manier om een verwijzingstoken met een autorisatieserver te verifiëren. Een onlangs voorgestelde standaard RFC 7662 is echter gepubliceerd door de IETF die definieert hoe een resourceserver de geldigheid van een token kan verifiëren.

Het token extraheren

De eerste stap is het extraheren van het token uit de autorisatieheader. De headerwaarde moet worden opgemaakt met het Bearer autorisatieschema, één spatie en vervolgens het autorisatietoken volgens RFC 6750. Helaas zijn er gevallen waarin het autorisatieschema wordt weggelaten. Als u dit wilt doen bij het parseren, splitst API Management de headerwaarde op een spatie en selecteert u de laatste tekenreeks uit de geretourneerde matrix met tekenreeksen. Dit biedt een tijdelijke oplossing voor ongeldig opgemaakte autorisatieheaders.

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

De validatieaanvraag indienen

Zodra API Management het autorisatietoken heeft, kan API Management de aanvraag indienen om het token te valideren. RFC 7662 roept dit proces introspectie aan en vereist dat u POST een HTML-formulier naar de introspection-resource. Het HTML-formulier moet ten minste een sleutel/waardepaar met de sleutel tokenbevatten. Deze aanvraag voor de autorisatieserver moet ook worden geverifieerd om ervoor te zorgen dat kwaadwillende clients geen trawling kunnen uitvoeren voor geldige 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>

Het antwoord controleren

Het response-variable-name kenmerk wordt gebruikt om toegang te verlenen tot het geretourneerde antwoord. De naam die in deze eigenschap is gedefinieerd, kan worden gebruikt als sleutel in de context.Variables woordenlijst voor toegang tot het IResponse object.

Vanuit het antwoordobject kunt u de hoofdtekst ophalen en RFC 7622 vertelt API Management dat het antwoord een JSON-object moet zijn en ten minste een eigenschap active moet bevatten die een booleaanse waarde is. Wanneer active waar is, wordt het token als geldig beschouwd.

Als de autorisatieserver het veld 'actief' niet bevat om aan te geven of het token geldig is, gebruikt u een hulpprogramma zoals Postman om te bepalen welke eigenschappen zijn ingesteld in een geldig token. Als een geldig tokenantwoord bijvoorbeeld een eigenschap met de naam 'expires_in' bevat, controleert u of deze eigenschapsnaam op deze manier bestaat in het antwoord van de autorisatieserver:

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

Rapportagefout

U kunt een <choose> beleid gebruiken om te detecteren of het token ongeldig is en zo ja, een 401-antwoord te retourneren.

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

Volgens RFC 6750, waarin wordt beschreven hoe bearer tokens moeten worden gebruikt, retourneert API Management ook een WWW-Authenticate header met het 401-antwoord. Www-Authenticate is bedoeld om een client te laten instrueren over het samenstellen van een correct geautoriseerde aanvraag. Vanwege de grote verscheidenheid aan benaderingen die mogelijk zijn met het OAuth2-framework, is het moeilijk om alle benodigde informatie te communiceren. Gelukkig zijn er inspanningen om clients te helpen ontdekken hoe aanvragen op een resourceserver correct kunnen worden geautoriseerd.

Uiteindelijke oplossing

Aan het einde krijgt u het volgende beleid:

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

Dit is slechts een van de vele voorbeelden van hoe het send-request beleid kan worden gebruikt om nuttige externe services te integreren in het proces van aanvragen en antwoorden die via de API Management-service stromen.

Antwoordsamenstelling

Het send-request beleid kan worden gebruikt voor het verbeteren van een primaire aanvraag naar een back-endsysteem, zoals u in het vorige voorbeeld hebt gezien, of kan worden gebruikt als een volledige vervanging voor de back-end-aanroep. Met deze techniek kunt u eenvoudig samengestelde resources maken die worden samengevoegd op basis van meerdere verschillende systemen.

Een dashboard bouwen

Soms wilt u informatie kunnen weergeven die bestaat in meerdere back-endsystemen, bijvoorbeeld om een dashboard te sturen. De KPI's zijn afkomstig van alle verschillende back-ends, maar u wilt liever geen directe toegang tot deze KPI's geven en het zou handig zijn als alle informatie in één aanvraag kan worden opgehaald. Misschien heeft een deel van de back-endgegevens eerst een segmentering en segmentering en een beetje opschonen nodig. Het in de cache opslaan van die samengestelde resource is handig om de back-endbelasting te verminderen, omdat u weet dat gebruikers een gewoonte hebben om de F5-sleutel te hameren om te zien of hun minder presterende metrische gegevens kunnen veranderen.

De resource vervagen

De eerste stap voor het bouwen van de dashboardresource is het configureren van een nieuwe bewerking in Azure Portal. Dit is een tijdelijke aanduiding voor het configureren van een samenstellingsbeleid voor het bouwen van de dynamische resource.

Dashboardbewerking

De aanvragen indienen

Zodra de bewerking is gemaakt, kunt u een specifiek beleid voor die bewerking configureren.

Schermopname van het scherm Beleidsbereik.

De eerste stap is het extraheren van queryparameters uit de binnenkomende aanvraag, zodat u deze naar de back-end kunt doorsturen. In dit voorbeeld toont het dashboard informatie op basis van een bepaalde periode en heeft daarom een fromDate en toDate parameter. U kunt het set-variable beleid gebruiken om de informatie uit de aanvraag-URL te extraheren.

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

Zodra u deze informatie hebt, kunt u aanvragen indienen bij alle back-endsystemen. Elke aanvraag bouwt een nieuwe URL met de parametergegevens en roept de respectieve server aan en slaat het antwoord op in een contextvariabele.

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

API Management verzendt deze aanvragen sequentieel.

Reageren

Als u het samengestelde antwoord wilt maken, kunt u het retourantwoordbeleid gebruiken. Het set-body element kan een expressie gebruiken om een nieuwe JObject samen te stellen met alle onderdeelweergaven die zijn ingesloten als eigenschappen.

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

Het volledige beleid ziet er als volgt uit:

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

Samenvatting

De Azure API Management-service biedt flexibele beleidsregels die selectief kunnen worden toegepast op HTTP-verkeer en die de samenstelling van back-endservices mogelijk maakt. Of u nu uw API-gateway wilt verbeteren met waarschuwingsfuncties, verificatie, validatiemogelijkheden of nieuwe samengestelde resources wilt maken op basis van meerdere back-endservices, het send-request en gerelateerde beleid bieden een wereld van mogelijkheden.