Külső szolgáltatások használata az Azure API Management szolgáltatásból

Minden API-kezelési szintre vonatkozik

Az Azure API Management szolgáltatásban elérhető szabályzatok számos hasznos munkát végezhetnek pusztán a bejövő kérés, a kimenő válasz és az alapvető konfigurációs információk alapján. Az API Management-szabályzatok külső szolgáltatásaival való interakció azonban sokkal több lehetőséget kínál.

Az előző cikkekben megismerkedett az Azure Event Hubs szolgáltatás naplózási, monitorozási és elemzési szolgáltatásával. Ez a cikk olyan szabályzatokat mutat be, amelyek lehetővé teszik bármely külső HTTP-alapú szolgáltatás használatát. Ezek a szabályzatok használhatók távoli események aktiválására vagy az eredeti kérés és válasz valamilyen módon történő módosítására szolgáló információk lekérésére.

Küldés egy-Way-Request

Lehetséges, hogy a legegyszerűbb külső interakció a fire-and-forget stílusú kérés, amely lehetővé teszi, hogy egy külső szolgáltatást értesítsenek valamilyen fontos eseményről. A vezérlési folyamat szabályzatával choose bármilyen feltételt észlelhet, amely érdekli. Ha a feltétel teljesül, külső HTTP-kérést is kezdeményezhet az egyirányú küldési szabályzat használatával. Ez a kérés irányulhat egy üzenetkezelő rendszerhez, például a Hipchat vagy a Slack, vagy egy e-mail API-hoz, mint a SendGrid vagy a MailChimp, vagy kritikus támogatási incidensek esetén a PagerDuty szolgáltatáshoz. Ezen üzenetkezelési rendszerek mindegyike egyszerű HTTP API-kkal rendelkezik, amelyek meghívhatók.

Riasztás a Slack-kal

Az alábbi példa bemutatja, hogyan küldhet üzenetet egy Slack-csevegőszobának, ha a HTTP-válasz állapotkódja nagyobb vagy egyenlő 500-nál. Az 500-es tartományhiba azt a problémát jelzi a háttér API-val kapcsolatban, amelyet az API ügyfele nem tud megoldani. Általában valamilyen beavatkozást igényel az API Management részéről.

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

A Slack rendelkezik a bejövő webhookok fogalmával. Bejövő webes horog konfigurálásakor a Slack létrehoz egy speciális URL-címet, amely lehetővé teszi egy alapszintű POST-kérést, és üzenetet továbbíthat a Slack-csatornába. A létrehozott JSON-törzs a Slack által meghatározott formátumon alapul.

Képernyőkép egy Slack Web Hookról.

A tűz és a felejtés elég jó?

Vannak bizonyos kompromisszumok használata esetén tűz-és felejtés stílusú kérés. Ha valamilyen okból a kérés meghiúsul, a rendszer nem jelenti a hibát. Ebben az esetben a másodlagos hibajelentési rendszer összetettsége és a válaszra való várakozás hozzáadott teljesítményköltsége nem indokolt. Azokban a forgatókönyvekben, ahol elengedhetetlen a válasz ellenőrzése, akkor a küldési-kérési szabályzat jobb megoldás.

Send-Request

A send-request szabályzat lehetővé teszi, hogy külső szolgáltatás használatával összetett feldolgozási függvényeket hajtson végre, és adatokat adjon vissza az API management szolgáltatásnak, amely további szabályzatfeldolgozáshoz használható.

Referencia-jogkivonatok engedélyezése

Az API Management egyik fő funkciója a háttérerőforrások védelme. Ha az API által használt engedélyezési kiszolgáló JSON-webjogkivonatokat (JWT-ket) hoz létre az OAuth2-folyamat részeként, ahogyan a Microsoft Entra ID is teszi, akkor a validate-jwt szabályzat vagy validate-azure-ad-token szabályzat használatával ellenőrizheti a jogkivonat érvényességét. Egyes engedélyezési kiszolgálók referencia-jogkivonatokat hoznak létre, amelyek nem ellenőrizhetők az engedélyezési kiszolgálóra való visszahívás nélkül.

Standardizált bevezető

Korábban nem volt szabványosított módszer egy referencia-jogkivonat hitelesítésére egy engedélyezési kiszolgálóval. Az Internet Engineering Task Force (IETF) azonban nemrég közzétette a javasolt standard RFC 7662-et , amely meghatározza, hogy egy erőforrás-kiszolgáló hogyan ellenőrizheti egy jogkivonat érvényességét.

A jogkivonat kinyerése

Az első lépés a token kinyerése az Authorization fejlécből. A fejléc értékét az Bearer engedélyezési sémával, egyetlen szóközzel, majd az engedélyezési jogkivonattal kell formázni az RFC 6750 szerint. Sajnos vannak olyan esetek, amikor az engedélyezési sémát kihagyják. Ha ezt a kihagyást szeretné figyelembe venni az elemzés során, az API Management felosztja a fejléc értékét egy szóközön, és kiválasztja az utolsó sztringet a visszaadott sztringtömbből. Ez a módszer kerülő megoldást kínál a hibásan formázott engedélyezési fejlécekhez.

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

Az érvényesítési kérelem végrehajtása

Ha az API Management rendelkezik az engedélyezési jogkivonattal, az API Management kérheti a jogkivonat érvényesítését. Az RFC 7662 ezt a folyamatot introspekciónak nevezi, és megköveteli, hogy egy HTML-űrlapot küldjön az introspekciós erőforráshoz. A HTML-űrlapnak tartalmaznia kell legalább egy kulcsot/értékpárt a kulccsal token. Az engedélyezési kiszolgáló felé irányuló kérést is hitelesíteni kell, hogy a rosszindulatú ügyfelek ne férhessenek hozzá az érvényes jogkivonatokhoz.

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

A válasz ellenőrzése

Az response-variable-name attribútum a visszaadott válaszhoz való hozzáférésre szolgál. A tulajdonságban definiált név kulcsként használható a szótárban az context.VariablesIResponse objektum eléréséhez.

A válaszobjektumból lekérheti a törzset, és az RFC 7622 tájékoztatja az API Managementet, hogy a válasznak JSON-objektumnak kell lennie, és tartalmaznia kell legalább egy logikai értéket tartalmazó active tulajdonságot. Ha active igaz, a token érvényesnek minősül.

Ha az engedélyezési kiszolgáló nem tartalmazza a "active" jogkivonat érvényességét jelző mezőt, használjon EGY HTTP-ügyféleszközt, például curl annak meghatározásához, hogy milyen tulajdonságok vannak beállítva egy érvényes jogkivonatban. Ha például egy érvényes jogkivonat-válasz tartalmaz egy nevű "expires_in"tulajdonságot, ellenőrizze, hogy ez a tulajdonságnév szerepel-e az engedélyezési kiszolgáló válaszában így:

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

Jelentéskészítési hiba

Használhat szabályzatot a <choose> segítségével, hogy észlelje, ha a jogkivonat érvénytelen, és ebben az esetben egy 401-es hibaüzenetet adjon vissza.

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

Az RFC 6750 szerint, amely leírja, hogyan kell használni a bearer jogkivonatokat, az API Management egy WWW-Authenticate fejlécet is visszaad a 401-es válasszal. A WWW-Authenticate arra szolgál, hogy az ügyfelet arra utasítsa, hogyan hozhat létre megfelelően engedélyezett kérést. Az OAuth2 keretrendszerrel lehetséges megközelítések széles választéka miatt nehéz minden szükséges információt közölni. Szerencsére vannak folyamatban lévő erőfeszítések annak érdekében, hogy az ügyfelek felfedezzék, hogyan engedélyezhetik megfelelően az erőforrás-kiszolgálóra irányuló kéréseket.

Végső megoldás

A végén a következő szabályzat jelenik meg:

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

Ez a példa csak egy a sok közül, amely bemutatja, hogyan használható a szabályzat hasznos send-request külső szolgáltatások integrálására az API Management szolgáltatáson keresztüli kérések és válaszok folyamatába.

Válaszösszeállítás

A send-request szabályzat használható a háttérrendszerre irányuló elsődleges kérések továbbfejlesztésére, ahogy az előző példában is láthatta, vagy a háttérhívás teljes cseréjére is használható. Ezzel a technikával egyszerűen létrehozhat összetett erőforrásokat, amelyek több különböző rendszerből vannak összesítve.

Irányítópult létrehozása

Előfordulhat, hogy több háttérrendszerben található információkat szeretne elérhetővé tenni, például egy irányítópult vezetéséhez. A fő teljesítménymutatók (KPI-k) minden különböző háttérrendszerből származnak, de nem szeretne közvetlen hozzáférést biztosítani hozzájuk. Mégis jó lenne, ha az összes információt egyetlen kérelemben lehetne lekérni. Talán néhány háttérinformációhoz előbb szeletelésre, darabolásra és egy kis tisztításra van szükség! Az összetett erőforrás gyorsítótárazása hasznos módszer lenne a háttérterhelés csökkentésére, mivel tudjuk, hogy a felhasználóknak szokása az F5 billentyűt ütögetni, hogy lássák, megváltozhatnak-e az alulteljesítő metrikáik.

Az erőforrás meghamisítása

Az irányítópult-erőforrás létrehozásának első lépése egy új művelet konfigurálása az Azure Portalon. Ez a helyőrző művelet egy összeállítási szabályzat konfigurálására szolgál a dinamikus erőforrás létrehozásához.

Képernyőkép az Azure Portalon konfigurált új irányítópult-műveletről.

A kérések elkészítése

A művelet létrehozása után konfigurálhat egy szabályzatot kifejezetten ehhez a művelethez.

Képernyőkép a Szabályzat hatókörének képernyőről.

Az első lépés a lekérdezési paraméterek kinyerése a bejövő kérelemből, hogy továbbíthassa őket a háttérrendszernek. Ebben a példában az irányítópult egy adott időszakon alapuló információkat jelenít meg, ezért rendelkezik egy fromDate és egy toDate paraméterrel. A szabályzat segítségével set-variable kinyerheti az adatokat a kérelem URL-címéből.

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

Miután megkapta ezeket az információkat, kéréseket intézhet az összes háttérrendszerhez. Minden kérés létrehoz egy új URL-címet a paraméteradatokkal, meghívja a megfelelő kiszolgálót, és egy környezeti változóban tárolja a választ.

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

Az API Management egymás után küldi el ezeket a kéréseket.

Válaszolás

Az összetett válasz létrehozásához használhatja a visszatérési-válasz szabályzatot. A(z) set-body elem használhat egy kifejezést, hogy egy új JObject-t hozzon létre, amely minden összetevő ábrázolását tulajdonságként ágyazza be.

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

A teljes szabályzat a következőképpen néz ki:

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

Összefoglalás

Az Azure API Management szolgáltatás rugalmas szabályzatokat biztosít, amelyek szelektíven alkalmazhatók a HTTP-forgalomra, és lehetővé teszik a háttérszolgáltatások összetételét. Akár riasztási funkciókkal, ellenőrzéssel, érvényesítési képességekkel szeretné továbbfejleszteni az API-átjárót, akár több háttérszolgáltatáson alapuló új összetett erőforrásokat szeretne létrehozni, a send-request kapcsolódó szabályzatok a lehetőségek világát nyitják meg.