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 Hubs-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.
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 token
bevatten. 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 HTTP-clienthulpprogramma, bijvoorbeeld curl
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.
De aanvragen indienen
Zodra de bewerking is gemaakt, kunt u een specifiek beleid voor die bewerking configureren.
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.