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.We have previously seen how we can interact with the Azure Event Hub service for logging, monitoring and analytics. In questo articolo verranno descritti i criteri che consentono di interagire con qualsiasi servizio esterno basato su HTTP.In this article we will demonstrate 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 verranno usate per gestire la richiesta e la risposta originali.These policies can be used for triggering remote events or for retrieving information that will be 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 i criteri del flusso di controllo choose per rilevare qualsiasi tipo di condizione di interesse e quindi, se la condizione è soddisfatta, inviare una richiesta HTTP esterna usando i criteri send-one-way-request .We can use the control flow policy choose to detect any kind of condition that we are interested in and then, if the condition is satisfied, we 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 possono essere richiamate facilmente.All of these messaging systems have simple HTTP APIs that we can easily invoke.

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 our backend API that the client of our API cannot resolve themselves. In genere richiede un intervento da parte dell'utente.It usually requires some kind of intervention on our 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 hook Web 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 we 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 will 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. Tuttavia, alcuni server di autorizzazione creano i cosiddetti token di riferimento , che non possono essere verificati senza eseguire il callback al server di autorizzazione.However, some authorization servers create what are called reference tokens that cannot be verified without making a call back 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 il valore dell'intestazione viene suddiviso su uno spazio e viene selezionata l'ultima stringa dalla matrice di stringhe restituita.To account for this when parsing, we split the header value on a space and select 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 aver ottenuto il token di autorizzazione, è possibile inviare la richiesta per la convalida del token.Once we have the authorization token, we 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 a 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 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 we can retrieve the body and RFC 7622 tells us 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, si usano i criteri <choose> e, in questo caso, viene restituita una risposta 401.We 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, viene inoltre restituita un'intestazione WWW-Authenticate con la risposta 401.As per RFC 6750 which describes how bearer tokens should be used, we also return 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

Combinando tutte le parti descritte, si ottengono i criteri seguenti:Putting all the pieces together, we 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 we 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 we 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 pubblicazione di Gestione API.The first step to building our dashboard resource is to configure a new operation in the API Management publisher portal. Si tratta di un'operazione di segnaposto usata per configurare i criteri di composizione per creare la risorsa dinamica.This will be a placeholder operation used to configure our composition policy to build our dynamic resource.

Operazione dashboard

Invio delle richiesteMaking the requests

Dopo aver creato l'operazione dashboard , è possibile configurare criteri specifici per tale operazione.Once the dashboard operation has been created we 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 we can forward them to our 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 our dashboard is showing information based on a period of time an therefore has a fromDate and toDate parameter. È possibile usare i criteri set-variable per estrarre le informazioni dall'URL della richiesta.We 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 le richieste a tutti i sistemi back-end.Once we have this information we 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 will execute in sequence, which is not ideal. In una versione futura verranno introdotti nuovi criteri denominati wait che consentiranno l'esecuzione in parallelo di tutte le richieste.In an upcoming release we will be introducing a new policy called wait that will enable all of these requests to execute in parallel.

Invio delle risposteResponding

Per costruire la risposta composita è possibile usare i criteri return-response .To construct the composite response we 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 impostare la risorsa dashboard in modo che venga memorizzata nella cache per almeno un'ora, perché la natura dei dati implica che anche se scaduta, è comunque sufficientemente efficace per fornire informazioni utili agli utenti.In the configuration of the placeholder operation we can configure the dashboard resource to be cached for at least an hour because we understand the nature of the data means that even if it is an hour out of date, it will still be sufficiently effective to convey valuable information to the users.

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.

Video contenente una panoramica di questi criteriWatch a video overview of these policies

Per altre informazioni sui criteri send-one-way-request, send-request e return-response presentati in questo articolo, guardare il video seguente.For more information on the send-one-way-request, send-request, and return-response policies covered in this article, please watch the following video.