Uso di servizi esterni dal servizio Gestione API di AzureUsing external services from the Azure API Management service

I criteri disponibili nel servizio Gestione API di Azure possono essere usati per una vasta gamma di attività basate esclusivamente su richieste in ingresso, risposte in uscita e informazioni di configurazione di base.The policies available in Azure API Management service can do a wide range of useful work based purely on the incoming request, the outgoing response, and basic configuration information. Tuttavia, la possibilità di interagire con i servizi esterni dai criteri di Gestione API offre molte altre opportunità.However, being able to interact with external services from API Management policies opens up many more opportunities.

In precedenza è stata analizzata l'interazione con il servizio Hub eventi di Azure per la registrazione, il monitoraggio e l'analisi.You have previously seen how to interact with the Azure Event Hub service for logging, monitoring, and analytics. In questo articolo vengono descritti i criteri che consentono di interagire con qualsiasi servizio esterno basato su HTTP.This article demonstrates policies that allow you to interact with any external HTTP-based service. Questi criteri possono essere usati per l'attivazione di eventi remoti o per il recupero di informazioni che vengono usate per gestire la richiesta e la risposta originali.These policies can be used for triggering remote events or for retrieving information that is used to manipulate the original request and response in some way.

Send-One-Way-RequestSend-One-Way-Request

Probabilmente l'interazione esterna più semplice è lo stile fire-and-forget della richiesta, che consente a un servizio esterno di ricevere una notifica se si verifica un evento importante.Possibly the simplest external interaction is the fire-and-forget style of request that allows an external service to be notified of some kind of important event. È possibile usare il criterio di flusso di controllo choose per rilevare qualsiasi tipo di condizione cui si è interessati.The control flow policy choose can be used to detect any kind of condition that you are interested in. Se la condizione è soddisfatta, è possibile inviare una richiesta HTTP esterna usando i criteri send-one-way-request.If the condition is satisfied, you can make an external HTTP request using the send-one-way-request policy. Può trattarsi di una richiesta a un sistema di messaggistica, ad esempio Hipchat o Slack, o a un'API di posta elettronica, ad esempio SendGrid o MailChimp, o per eventi che richiedono interventi di supporto critico, ad esempio PagerDuty.This could be a request to a messaging system like Hipchat or Slack, or a mail API like SendGrid or MailChimp, or for critical support incidents something like PagerDuty. Tutti questi sistemi di messaggistica dispongono di API HTTP semplici che è possibile richiamare.All of these messaging systems have simple HTTP APIs that can be invoked.

Avvisi con SlackAlerting with Slack

L'esempio seguente illustra come inviare un messaggio a una chat di Slack, se il codice di stato della risposta HTTP è maggiore o uguale a 500.The following example demonstrates how to send a message to a Slack chat room if the HTTP response status code is greater than or equal to 500. Un errore di intervallo 500 indica un problema con l'API back-end che il client dell'API non può risolvere automaticamente.A 500 range error indicates a problem with the backend API that the client of the API cannot resolve themselves. In genere richiede un intervento da parte di Gestione API.It usually requires some kind of intervention on API Management part.

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

In Slack sono disponibili gli hook Web in ingresso.Slack has the notion of inbound web hooks. Quando si configura un webhook in ingresso, Slack genera un URL specifico che consente di eseguire una semplice richiesta POST e di passare un messaggio nel canale di Slack.When configuring an inbound web hook, Slack generates a special URL, which allows you to do a simple POST request and to pass a message into the Slack channel. Il corpo JSON creato è basato su un formato definito da Slack.The JSON body that you create is based on a format defined by Slack.

Hook Web di Slack

Lo stile di richiesta fire-and-forget è sufficientemente efficace?Is fire and forget good enough?

Quando si usa uno stile di richiesta fire-and-forget è necessario tenere presenti alcuni svantaggi.There are certain tradeoffs when using a fire-and-forget style of request. Se per qualche motivo la richiesta ha esito negativo, l'errore non viene segnalato.If for some reason, the request fails, then the failure is not be reported. In questo caso, la complessità di disporre di un sistema secondario di segnalazione dell'errore e il costo aggiuntivo delle prestazioni per l'attesa della risposta non sono garantiti.In this particular situation, the complexity of having a secondary failure reporting system and the additional performance cost of waiting for the response is not warranted. Per gli scenari in cui è fondamentale verificare la risposta, i criteri send-request rappresentano un'opzione migliore.For scenarios where it is essential to check the response, then the send-request policy is a better option.

send-requestSend-Request

I criteri send-request consentono l'utilizzo di un servizio esterno per eseguire funzioni di elaborazione complesse e restituire dati al servizio Gestione API, che può essere usato per un'elaborazione successiva dei criteri.The send-request policy enables using an external service to perform complex processing functions and return data to the API management service that can be used for further policy processing.

Autorizzazione di token di riferimentoAuthorizing reference tokens

Una funzione fondamentale di Gestione API è la protezione delle risorse back-end.A major function of API Management is protecting backend resources. Se il server di autorizzazione usato dall'API crea token JWT come parte del flusso OAuth2, in modo analogo ad Azure Active Directory, è possibile usare i criteri validate-jwt per verificare la validità del token.If the authorization server used by your API creates JWT tokens as part of its OAuth2 flow, as Azure Active Directory does, then you can use the validate-jwt policy to verify the validity of the token. Alcuni server di autorizzazione creano i cosiddetti token di riferimento, che non possono essere verificati senza eseguire il callback al server di autorizzazione.Some authorization servers create what are called reference tokens that cannot be verified without making a callback to the authorization server.

Introspezione standardizzataStandardized introspection

In passato non era disponibile alcuna modalità standardizzata per verificare un token di riferimento con un server di autorizzazione.In the past, there has been no standardized way of verifying a reference token with an authorization server. L'IETF, tuttavia, ha recentemente proposto e pubblicato lo standard RFC 7662 , che definisce le modalità con cui un server di risorse può verificare la validità di un token.However a recently proposed standard RFC 7662 was published by the IETF that defines how a resource server can verify the validity of a token.

Estrazione del tokenExtracting the token

Il primo passaggio consiste nell'estrarre il token dall'intestazione di autorizzazione.The first step is to extract the token from the Authorization header. Il valore dell'intestazione deve essere costituito dallo schema di autorizzazione Bearer, uno spazio singolo e il token di autorizzazione, in base allo standard RFC 6750.The header value should be formatted with the Bearer authorization scheme, a single space, and then the authorization token as per RFC 6750. Purtroppo esistono casi in cui lo schema di autorizzazione viene omesso.Unfortunately there are cases where the authorization scheme is omitted. Per questo motivo, durante l'analisi Gestione API suddivide il valore dell'intestazione su uno spazio e seleziona l'ultima stringa dalla matrice di stringhe restituita.To account for this when parsing, API Management splits the header value on a space and selects the last string from the returned array of strings. Questa rappresenta una soluzione alternativa per le intestazioni di autorizzazione con formato non corretto.This provides a workaround for badly formatted authorization headers.

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

Invio della richiesta di convalidaMaking the validation request

Dopo che Gestione API ha ottenuto il token di autorizzazione, può inviare la richiesta per la convalida del token.Once API Management has the authorization token, API Management can make the request to validate the token. Lo standard RFC 7662 definisce questa procedura introspezione e richiede che venga inviata una richiesta POST per un modulo HTML alla risorsa di introspezione.RFC 7662 calls this process introspection and requires that you POST an HTML form to the introspection resource. Il modulo HTML deve contenere almeno una coppia chiave/valore con la chiave token.The HTML form must at least contain a key/value pair with the key token. La richiesta al server di autorizzazione deve essere autenticata anche per assicurarsi che client non autorizzati non vengano passati come token validi.This request to the authorization server must also be authenticated, to ensure that malicious clients cannot go trawling for valid 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>

Verifica della rispostaChecking the response

L'attributo response-variable-name viene usato per concedere l'accesso alla risposta restituita.The response-variable-name attribute is used to give access the returned response. Il nome definito in questa proprietà può essere usato come chiave nel dizionario context.Variables per accedere all'oggetto IResponse.The name defined in this property can be used as a key into the context.Variables dictionary to access the IResponse object.

Dall'oggetto della risposta è possibile recuperare il corpo e lo standard RFC 7622 indica a Gestione API che la risposta deve essere un oggetto JSON e deve contenere almeno una proprietà denominata active che rappresenta un valore booleano.From the response object, you can retrieve the body and RFC 7622 tells API Management that the response must be a JSON object and must contain at least a property called active that is a boolean value. Se active è true, il token è considerato valido.When active is true then the token is considered valid.

Creazione di report sull'erroreReporting failure

Per individuare un token non valido e, in tal caso restituire una risposta 401, è possibile usare i criteri <choose>.You can use a <choose> policy to detect if the token is invalid and if so, return a 401 response.

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

In base allo standard RFC 6750, in cui viene descritto come usare i token bearer, Gestione API restituisce anche un'intestazione WWW-Authenticate con la risposta 401.As per RFC 6750 which describes how bearer tokens should be used, API Management also returns a WWW-Authenticate header with the 401 response. L'intestazione WWW-Authenticate è progettata per indicare a un client come realizzare una richiesta correttamente autorizzata.The WWW-Authenticate is intended to instruct a client on how to construct a properly authorized request. A causa dell'ampia gamma di approcci possibili con il framework di OAuth2, è difficile comunicare tutte le informazioni necessarie.Due to the wide variety of approaches possible with the OAuth2 framework, it is difficult to communicate all the needed information. Fortunatamente, sono disponibili soluzioni che consentono ai client di definire le modalità per autorizzare correttamente le richieste a un server di risorse.Fortunately there are efforts underway to help clients discover how to properly authorize requests to a resource server.

Soluzione finaleFinal solution

Al termine, si ottengono i criteri seguenti:At the end, you get the following policy:

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

Questo è solo uno dei numerosi esempi che illustrano come usare i criteri send-request per integrare utili servizi esterni nel processo delle richieste e delle risposte che passano attraverso il servizio Gestione API.This is only one of many examples of how the send-request policy can be used to integrate useful external services into the process of requests and responses flowing through the API Management service.

Composizione della rispostaResponse Composition

I criteri send-request possono essere usati per l'ottimizzazione di una richiesta primaria a un sistema back-end, come illustrato nell'esempio precedente, oppure come sostituzione completa della chiamata al back-end.The send-request policy can be used for enhancing a primary request to a backend system, as you saw in the previous example, or it can be used as a complete replace for of the backend call. Questa tecnica consente di creare facilmente risorse composite che vengono aggregate da più sistemi.Using this technique you can easily create composite resources that are aggregated from multiple different systems.

Creazione di un dashboardBuilding a dashboard

A volte può essere utile saper esporre le informazioni presenti in più sistemi back-end, ad esempio, per creare un dashboard.Sometimes you want to be able to expose information that exists in multiple backend systems, for example, to drive a dashboard. Gli indicatori KPI provengono da diversi sistemi back-end, ma può essere opportuno evitare di fornire accesso diretto a questi ultimi e può essere utile recuperare tutte le informazioni in un'unica richiesta.The KPIs come from all different back-ends, but you would prefer not to provide direct access to them and it would be nice if all the information could be retrieved in a single request. Ad esempio, può essere innanzitutto necessario sezionare e ripulire alcune delle informazioni di back-end.Perhaps some of the backend information needs some slicing and dicing and a little sanitizing first! La possibilità di memorizzare nella cache tale risorsa complessa può essere utile per ridurre il carico del back-end, dato che gli utenti tendono a premere F5 per verificare se è possibile modificare le prestazioni limitate delle metriche.Being able to cache that composite resource would be a useful to reduce the backend load as you know users have a habit of hammering the F5 key in order to see if their underperforming metrics might change.

Simulazione della risorsaFaking the resource

Il primo passaggio per la creazione della risorsa dashboard consiste nella configurazione di una nuova operazione nel portale di Azure.The first step to building the dashboard resource is to configure a new operation in the Azure portal. Si tratta di un'operazione di segnaposto usata per configurare i criteri di composizione per creare la risorsa dinamica.This is a placeholder operation used to configure a composition policy to build the dynamic resource.

Operazione dashboard

Invio delle richiesteMaking the requests

Dopo aver creato l'operazione, è possibile configurare criteri specifici per tale operazione.Once the operation has been created, you can configure a policy specifically for that operation.

Operazione dashboard

Il primo passaggio consiste nell'estrarre eventuali parametri di query dalla richiesta in ingresso, in modo da poterli inoltrare al back-end.The first step is to extract any query parameters from the incoming request, so that you can forward them to the backend. In questo esempio il dashboard visualizza informazioni basate su un periodo di tempo e dispone quindi dei parametri fromDate e toDate.In this example, the dashboard is showing information based on a period of time and therefore has a fromDate and toDate parameter. È possibile usare i criteri set-variable per estrarre le informazioni dall'URL della richiesta.You can use the set-variable policy to extract the information from the request URL.

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

Quando le informazioni sono disponibili, è possibile inviare richieste a tutti i sistemi back-end.Once you have this information, you can make requests to all the backend systems. Ogni richiesta crea un nuovo URL con le informazioni sul parametro, chiama il rispettivo server e archivia la risposta in una variabile di contesto.Each request constructs a new URL with the parameter information and calls its respective server and stores the response in a context variable.

<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/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
  <set-method>GET</set-method>
</send-request>

Queste richieste vengono eseguite in sequenza, ma non si tratta della situazione ideale.These requests execute in sequence, which is not ideal.

Invio delle risposteResponding

Per costruire la risposta composita, è possibile usare i criteri return-response.To construct the composite response, you can use the return-response policy. L'elemento set-body può usare un'espressione per creare un nuovo oggetto JObject con tutte le rappresentazioni di componenti incorporate come proprietà.The set-body element can use an expression to construct a new JObject with all the component representations embedded as properties.

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

I criteri completi saranno simili ai seguenti:The complete policy looks as follows:

<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/throughput?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>

Nella configurazione dell'operazione segnaposto, è possibile configurare la risorsa del dashboard in modo che venga memorizzata nella cache per almeno un'ora.In the configuration of the placeholder operation, you can configure the dashboard resource to be cached for at least an hour.

SummarySummary

Il servizio Gestione API di Azure offre criteri flessibili che possono essere applicati in modo selettivo al traffico HTTP e consentono la realizzazione di servizi back-end.Azure API Management service provides flexible policies that can be selectively applied to HTTP traffic and enables composition of backend services. Se si desidera migliorare il gateway API con funzioni di avviso, verifica e convalida o creare nuove risorse complesse basate su più servizi back-end, send-request e i criteri correlati offrono numerose possibilità.Whether you want to enhance your API gateway with alerting functions, verification, validation capabilities or create new composite resources based on multiple backend services, the send-request and related policies open a world of possibilities.