Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
GÄLLER FÖR: Alla API Management-nivåer
De principer som är tillgängliga i Azure API Management-tjänsten kan utföra ett brett utbud av användbart arbete baserat enbart på inkommande begäran, utgående svar och grundläggande konfigurationsinformation. Men att kunna interagera med externa tjänster från API Management-principer öppnar många fler möjligheter.
I tidigare artiklar såg du hur du interagerar med Azure Event Hubs-tjänsten för loggning, övervakning och analys. Den här artikeln visar principer som gör att du kan interagera med alla externa HTTP-baserade tjänster. Dessa principer kan användas för att utlösa fjärrhändelser eller för att hämta information som används för att ändra den ursprungliga begäran och det ursprungliga svaret på något sätt.
Skicka-enkelriktad-förfrågan
Möjligen är det enklaste externa sättet att interagera en fire-and-forget-begäran som gör att en extern tjänst kan meddelas om en viktig händelse. Kontrollflödesprincipen choose kan användas för att identifiera alla typer av villkor som du är intresserad av. Om villkoret är uppfyllt kan du göra en extern HTTP-begäran med hjälp av principen send-one-way-request . Den här begäran kan vara till ett meddelandesystem som Hipchat eller Slack, eller ett e-post-API som SendGrid eller MailChimp, eller för kritiska supportincidenter som PagerDuty. Alla dessa meddelandesystem har enkla HTTP-API:er som kan anropas.
Aviseringar med Slack
I följande exempel visas hur du skickar ett meddelande till ett Slack-chattrum om HTTP-svarsstatuskoden är större än eller lika med 500. Ett 500-intervallfel anger ett problem med serverdels-API:et som klienten för API:et inte kan lösa själva. Det kräver vanligtvis någon form av åtgärder från API Management-delen.
<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 har begreppet inkommande webbkrokar. När den konfigurerar en inkommande webbkrok genererar Slack en särskild URL som gör att du kan göra en grundläggande POST-begäran och skicka ett meddelande till Slack-kanalen. JSON-brödtexten som du skapar baseras på ett format som definierats av Slack.
              
               
              
              
            
Är eld och glöm tillräckligt bra?
Det finns vissa kompromisser när du använder en fire-and-forget-typ av begäran. Om begäran av någon anledning misslyckas rapporteras inte felet. I så fall är komplexiteten i ett sekundärt felrapporteringssystem och den extra prestandakostnaden för att vänta på svaret inte berättigad. För scenarier där det är viktigt att kontrollera svaret är principen för att skicka begäran ett bättre alternativ.
Skicka begäran
Principen send-request gör det möjligt att använda en extern tjänst för att utföra komplexa bearbetningsfunktioner och returnera data till API-hanteringstjänsten som kan användas för ytterligare principbearbetning.
Auktorisera referenstoken
En viktig funktion i API Management är att skydda serverdelsresurser. Om auktoriseringsservern som används av ditt API skapar JSON-webbtoken (JWT) som en del av dess OAuth2-flöde, som Microsoft Entra-ID gör, kan du använda validate-jwt principen eller validate-azure-ad-token principen för att verifiera tokens giltighet. Vissa auktoriseringsservrar skapar det som kallas referenstoken som inte kan verifieras utan att göra ett återanrop till auktoriseringsservern.
Standardiserad introspektion
Tidigare fanns det inget standardiserat sätt att verifiera en referenstoken med en auktoriseringsserver. IETF (Internet Engineering Task Force) publicerade dock nyligen den föreslagna standarden RFC 7662 som definierar hur en resursserver kan verifiera giltigheten för en token.
Extrahera tokenet
Det första steget är att extrahera token från Authorization-header. Rubrikvärdet ska formateras med Bearer auktoriseringsschemat, ett enda blanksteg och sedan auktoriseringstoken enligt RFC 6750. Tyvärr finns det fall där auktoriseringsschemat utelämnas. För att hantera detta utelämnande vid parsning delar API Management upp huvudvärdet vid ett blanksteg och väljer den sista strängen från den returnerade arrayen med strängar. Den här metoden ger en lösning för felformaterade auktoriseringshuvuden.
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
Göra valideringsbegäran
När API Management har auktoriseringstoken kan API Management göra begäran för att verifiera token. RFC 7662 kallar denna process för introspektion och kräver att du POST skickar in ett HTML-formulär till resursen för introspektion. HTML-formuläret måste minst innehålla ett nyckel/värde-par med nyckeln token. Den här begäran till auktoriseringsservern måste också autentiseras för att säkerställa att skadliga klienter inte kan tråla efter giltiga token.
<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>
Kontrollera svaret
Attributet response-variable-name används för att ge åtkomst till det returnerade svaret. Namnet som definieras i den här egenskapen kan användas som en nyckel i context.Variables ordlistan för att komma åt IResponse objektet.
Från svarsobjektet kan du hämta brödtexten och RFC 7622 meddelar API Management att svaret måste vara ett JSON-objekt och måste innehålla minst en egenskap som heter active som är ett booleskt värde. När active är sant anses token vara giltig.
Om auktoriseringsservern inte innehåller fältet "active" för att ange om token är giltig, använd ett HTTP-klientverktyg som curl för att avgöra vilka egenskaper som anges i en giltig token. Om ett giltigt tokensvar till exempel innehåller en egenskap med namnet "expires_in"kontrollerar du om det här egenskapsnamnet finns i auktoriseringsserverns svar på det här sättet:
<when condition="@(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>().Property("expires_in") == null)">
Rapporteringsfel
Du kan använda en <choose> princip för att identifiera om token är ogiltig och i så fall returnera ett 401-svar.
<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>
Enligt RFC 6750, som beskriver hur bearer token ska användas, returnerar API Management även ett WWW-Authenticate huvud med 401-svaret. WWW-Authenticate är avsedd att instruera en klient om hur du skapar en korrekt auktoriserad begäran. På grund av de många olika metoder som är möjliga med OAuth2-ramverket är det svårt att kommunicera all nödvändig information. Lyckligtvis pågår det arbete för att hjälpa klienter att identifiera hur begäranden kan auktoriseras korrekt till en resursserver.
Slutlig lösning
I slutet får du följande princip:
<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>
Det här exemplet är bara ett av många som visar hur send-request principen kan användas för att integrera användbara externa tjänster i processen för begäranden och svar som flödar via API Management-tjänsten.
Svarssammansättning
Principen send-request kan användas för att förbättra en primär begäran till ett serverdelssystem, som du såg i föregående exempel, eller så kan den användas för att helt ersätta serverdelsanropet. Med den här tekniken kan du enkelt skapa sammansatta resurser som aggregeras från flera olika system.
Skapa en instrumentpanel
Ibland vill du kunna exponera information som finns i flera backend-system, till exempel för att driva en instrumentpanel. KPI:er (Key Performance Indicators) kommer från alla olika bakändar, men du föredrar att inte ge direkt åtkomst till dem. Ändå skulle det vara trevligt om all information kunde hämtas i en enda begäran. Kanske behöver en del av backend-informationen skäras i bitar och bearbetas och lite rensas först! Att kunna cachelagra den sammansatta resursen skulle vara ett användbart sätt att minska belastningen på backend eftersom du vet att användarna har för vana att trycka frenetiskt på F5-tangenten för att se om deras underpresterande mätvärden kan ändras.
Falska resursen
Det första steget för att skapa instrumentpanelsresursen är att konfigurera en ny åtgärd i Azure Portal. Den här platshållaråtgärden används för att konfigurera en sammansättningsprincip för att skapa den dynamiska resursen.
              
               
              
              
            
Göra begäranden
När åtgärden har skapats kan du konfigurera en princip specifikt för den åtgärden.
              
               
              
              
            
Det första steget är att extrahera alla frågeparametrar från den inkommande begäran så att du kan vidarebefordra dem till serverdelen. I det här exemplet visar instrumentbrädan information baserat på en tidsperiod och har därför parametrarna fromDate och toDate. Du kan använda set-variable principen för att extrahera informationen från begärande-URL:en.
<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">
När du har den här informationen kan du skicka förfrågningar till alla backend-system. Varje begäran skapar en ny URL med parameterinformationen och anropar respektive server och lagrar svaret i en kontextvariabel.
<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 skickar dessa begäranden sekventiellt.
Svara
Om du vill skapa det sammansatta svaret kan du använda principen return-response . Elementet set-body kan använda ett uttryck för att konstruera ett nytt JObject med alla komponentrepresentationer inbäddade som egenskaper.
<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>
Den fullständiga principen ser ut så här:
<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>
Sammanfattning
Azure API Management-tjänsten tillhandahåller flexibla principer som kan tillämpas selektivt på HTTP-trafik och möjliggör sammansättning av serverdelstjänster. Oavsett om du vill förbättra din API-gateway med aviseringsfunktioner, verifierings-, valideringsfunktioner eller skapa nya sammansatta resurser baserat på flera serverdelstjänster, öppnar och send-request relaterade principer en värld av möjligheter.