Použití externích služeb ze služby Azure API Management

PLATÍ PRO: Všechny úrovně služby API Management

Zásady dostupné ve službě Azure API Management můžou provádět širokou škálu užitečných prací čistě na příchozím požadavku, odchozí odpovědi a základních konfiguračních informacích. Schopnost pracovat s externími službami ze zásad služby API Management ale nabízí spoustu dalších příležitostí.

Dříve jste viděli, jak pracovat se službou Azure Event Hub pro protokolování, monitorování a analýzu. Tento článek ukazuje zásady, které umožňují interakci s jakoukoli externí službou založenou na protokolu HTTP. Tyto zásady se dají použít k aktivaci vzdálených událostí nebo k načítání informací, které slouží k manipulaci s původním požadavkem a odpovědí nějakým způsobem.

Odeslání jednosměrné žádosti

Možná nejjednodušší externí interakce je fire-and-zapomenutý styl požadavku, který umožňuje externí službě být upozorněn na nějaký druh důležité události. Zásady choose toku řízení se dají použít k detekci jakéhokoli druhu podmínky, kterou vás zajímají. Pokud je podmínka splněná, můžete vytvořit externí požadavek HTTP pomocí zásady odesílání jednosměrných požadavků . Může to být požadavek na systém zasílání zpráv, jako je Hipchat nebo Slack, nebo rozhraní API pošty, jako je SendGrid nebo MailChimp, nebo pro incidenty kritické podpory, jako je PagerDuty. Všechny tyto systémy zasílání zpráv mají jednoduchá rozhraní API HTTP, která je možné vyvolat.

Upozorňování pomocí Slacku

Následující příklad ukazuje, jak odeslat zprávu do chatovací místnosti Slack, pokud je stavový kód odpovědi HTTP větší nebo roven 500. Chyba rozsahu 500 značí problém s back-endovým rozhraním API, že se klient rozhraní API nemůže sám vyřešit. Obvykle vyžaduje nějaký zásah do služby API Management.

<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 má pojem příchozích webových háků. Při konfiguraci příchozího webového háku vygeneruje Slack speciální adresu URL, která umožňuje provést jednoduchý požadavek POST a předat zprávu do kanálu Slack. Text JSON, který vytvoříte, je založený na formátu definovaném slackem.

Slack Web Hook

Je oheň a zapomeňte dost dobře?

Existují určité kompromisy při použití typu fire-and-forget styl žádosti. Pokud z nějakého důvodu požadavek selže, selhání se neohlásí. V této konkrétní situaci není složitost systému generování zpráv o sekundárním selhání a dodatečné náklady na výkon čekání na odpověď zaručené. V situacích, kdy je nezbytné zkontrolovat odpověď, je lepší volbou zásada odeslání požadavku .

Odeslat žádost

Tato send-request zásada umožňuje používat externí službu k provádění složitých funkcí zpracování a vracení dat do služby API Management, kterou je možné použít k dalšímu zpracování zásad.

Autorizace referenčních tokenů

Hlavní funkcí služby API Management je ochrana back-endových prostředků. Pokud autorizační server používaný vaším rozhraním API vytvoří tokeny JWT jako součást toku OAuth2, jak to dělá Id Microsoft Entra , můžete pomocí validate-jwt zásad nebo validate-azure-ad-token zásad ověřit platnost tokenu. Některé autorizační servery vytvářejí tzv . referenční tokeny , které nelze ověřit bez zpětného volání autorizačního serveru.

Standardizovaná introspekce

V minulosti neexistuje žádný standardizovaný způsob ověření referenčního tokenu s autorizačním serverem. Nedávno navržený standard RFC 7662 byl publikován IETF, který definuje, jak může server prostředků ověřit platnost tokenu.

Extrahování tokenu

Prvním krokem je extrakce tokenu z autorizační hlavičky. Hodnota hlavičky by měla být naformátovaná schématem Bearer autorizace, jednou mezerou a potom autorizačním tokenem podle dokumentu RFC 6750. Bohužel existují případy, kdy je schéma autorizace vynecháno. Při analýze služba API Management rozdělí hodnotu hlavičky na mezeru a vybere poslední řetězec z vráceného pole řetězců. To poskytuje alternativní řešení pro špatně formátované autorizační hlavičky.

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

Vytvoření žádosti o ověření

Jakmile má služba API Management autorizační token, může služba API Management provést požadavek na ověření tokenu. RFC 7662 volá tuto introspekci procesu a vyžaduje, abyste POST formulář HTML do prostředku introspekce. Formulář HTML musí obsahovat alespoň pár klíč/hodnota s klíčem token. Tato žádost o autorizační server musí být také ověřena, aby se zajistilo, že klienti se zlými úmysly nemohou procházet platnými tokeny.

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

Kontrola odpovědi

Atribut response-variable-name slouží k udělení přístupu k vrácené odpovědi. Název definovaný v této vlastnosti lze použít jako klíč do slovníku context.Variables pro přístup k objektu IResponse .

Z objektu odpovědi můžete načíst tělo a RFC 7622 říká službě API Management, že odpověď musí být objekt JSON a musí obsahovat alespoň vlastnost, která se nazývá active logická hodnota. Pokud active je token pravdivý, považuje se za platný.

Pokud autorizační server neobsahuje pole "aktivní" označující, jestli je token platný, použijte nástroj, jako je Postman, a určete, jaké vlastnosti jsou nastaveny v platném tokenu. Pokud například platná odpověď tokenu obsahuje vlastnost s názvem "expires_in", zkontrolujte, jestli tento název vlastnosti existuje v odpovědi autorizačního serveru tímto způsobem:

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

Hlášení selhání

Pomocí zásady můžete <choose> zjistit, jestli je token neplatný a pokud ano, vrátit odpověď 401.

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

Podle dokumentu RFC 6750, který popisuje, jak bearer se mají používat tokeny, vrátí služba API Management také hlavičku WWW-Authenticate s odpovědí 401. Www-Authenticate je určen k pokynu klientovi, jak vytvořit správně autorizovaný požadavek. Vzhledem k široké škále možných přístupů s architekturou OAuth2 je obtížné sdělit všechny potřebné informace. Naštěstí probíhá úsilí o pomoc klientům zjistit, jak správně autorizovat požadavky na server prostředků.

Konečné řešení

Na konci získáte následující zásady:

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

Toto je jen jeden z mnoha příkladů toho, jak send-request se zásady dají použít k integraci užitečných externích služeb do procesu požadavků a odpovědí procházejících službou API Management.

Složení odpovědi

Zásady send-request se dají použít k vylepšení primárního požadavku na back-endový systém, jak jste viděli v předchozím příkladu, nebo ji můžete použít jako úplnou náhradu pro back-endové volání. Pomocí této techniky můžete snadno vytvořit složené prostředky, které se agregují z více různých systémů.

Vytvoření řídicího panelu

Někdy chcete mít možnost zveřejnit informace, které existují v několika back-endových systémech, například pro řízení řídicího panelu. Klíčové ukazatele výkonu pocházejí ze všech různých back-endů, ale raději byste k nim neměli přímý přístup a bylo by hezké, kdyby se všechny informace mohly načíst v jediné žádosti. Možná některé z back-endových informací potřebují nějaké řezy a didikace a trochu sanitizace jako první! Schopnost ukládat tento složený prostředek do mezipaměti by byla užitečná ke snížení zatížení back-endu, protože víte, že uživatelé mají zvyk zatěžovat klíč F5, aby zjistili, jestli se jejich metriky pod výkonem můžou změnit.

Faking the resource

Prvním krokem k vytvoření prostředku řídicího panelu je konfigurace nové operace na webu Azure Portal. Jedná se o zástupnou operaci, která slouží ke konfiguraci zásad složení pro sestavení dynamického prostředku.

Operace řídicího panelu

Vytváření požadavků

Po vytvoření operace můžete nakonfigurovat zásady speciálně pro danou operaci.

Snímek obrazovky znázorňující obrazovku oboru zásad

Prvním krokem je extrakce parametrů dotazu z příchozího požadavku, abyste je mohli předat do back-endu. V tomto příkladu řídicí panel zobrazuje informace na základě časového období, a proto má fromDate a toDate parametr. Zásady můžete použít set-variable k extrahování informací z adresy URL požadavku.

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

Jakmile budete mít tyto informace, můžete vyhovět všem back-endovým systémům. Každý požadavek vytvoří novou adresu URL s informacemi o parametru a zavolá příslušný server a uloží odpověď do kontextové proměnné.

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

SLUŽBA API Management bude tyto požadavky posílat postupně.

Reagovat

K vytvoření složené odpovědi můžete použít zásadu návratové odpovědi . Element set-body může použít výraz k vytvoření nového JObject se všemi reprezentacemi součástí vloženými jako vlastnosti.

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

Úplná zásada vypadá takto:

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

Shrnutí

Služba Azure API Management poskytuje flexibilní zásady, které se dají selektivně použít na provoz HTTP a umožňují složení back-endových služeb. Bez ohledu na to, jestli chcete vylepšit bránu rozhraní API pomocí funkcí pro upozorňování, ověřování, ověřování nebo vytvářet nové složené prostředky založené na několika back-endových službách, send-request a souvisejících zásadách otevírají svět možností.