Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
SE APLICA A: Todos los niveles de API Management
Las directivas disponibles en el servicio Azure API Management pueden llevar a cabo una gran variedad de trabajo útil basado exclusivamente en la solicitud entrante, la respuesta saliente y la información de configuración básica. Pero la interacción con servicios externos de las directivas de API Management brinda muchas más oportunidades.
En los artículos anteriores, ha visto cómo interactuar con el servicio Azure Event Hubs para el registro, la supervisión y el análisis. En este artículo encontrará las directivas que permiten interactuar con cualquier servicio externo basado en HTTP. Dichas directivas se pueden usar para desencadenar eventos remotos o recuperar información que se utiliza para manipular en cierto modo la solicitud y la respuesta originales.
Send-One-Way-Request
Posiblemente, la interacción externa más sencilla es el estilo de solicitud lanzar y olvidar que permite que un servicio externo se informe de algún tipo de evento importante. La directiva choose de flujo de control se puede usar para detectar cualquier tipo de condición que le interese. Si se cumple la condición, puede hacer una solicitud HTTP externa usando la directiva Envío de solicitud unidireccional. Esta solicitud podría ser para un sistema de mensajería como Hipchat o Slack, o una API de correo como SendGrid o MailChimp, o para incidentes de soporte técnico críticos algo como PagerDuty. Todos estos sistemas de mensajería tienen API HTTP sencillas que se pueden invocar.
Alerta con Slack
En el siguiente ejemplo puede ver cómo enviar un mensaje a un salón de chat de Slack si el código de estado de respuesta HTTP es mayor o igual que 500. Un error de rango 500 indica un problema con la API de backend que el cliente de la API no puede resolver. Normalmente requiere algún tipo de intervención por parte de la gestión de API.
<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 tiene la noción de enlaces web entrantes. Cuando configura un enlace web entrante, Slack genera una dirección URL especial, lo que le permite realizar una solicitud POST básica y pasar un mensaje al canal de Slack. El cuerpo JSON creado se basa en un formato definido por Slack.
¿Es fire and forget lo suficientemente bueno?
Existen ciertos compromisos cuando se usa un estilo de fire and forget de solicitud. Si por algún motivo, se produce un error en la solicitud, no se notifica el error. En esa situación, la complejidad de un sistema de informes de fallos secundarios y el costo adicional en el rendimiento al esperar la respuesta no están justificados. En escenarios en los que es esencial comprobar la respuesta, la directiva de solicitud de envío es una mejor opción.
send-request
La directiva send-request permite usar un servicio externo para realizar funciones complejas de procesamiento y devolver datos al servicio de Administración de API que pueden usarse para un posterior procesamiento de directivas.
Autorización de tokens de referencia
Una de las funciones principales de API Management es proteger los recursos de back-end. Si el servidor de autorización usado por la API crea tokens web JSON (JWT) como parte del flujo OAuth2, como hace Microsoft Entra ID, entonces puede usar la validate-jwt directiva o la validate-azure-ad-token directiva para comprobar la validez del token. Algunos servidores de autorización crean los llamados tokens de referencia que no se pueden comprobar sin realizar una consulta al servidor de autorización.
Introspección estandarizada
En el pasado, no había ninguna manera estandarizada de comprobar un token de referencia con un servidor de autorización. Sin embargo, el Grupo de Tareas de Ingeniería de Internet (IETF) publicó recientemente el estándar propuesto RFC 7662 que define cómo un servidor de recursos puede comprobar la validez de un token.
Extracción del token
El primer paso es extraer el token del encabezado de autorización. El formato del valor de encabezado debe constar del esquema de autorización Bearer, un solo espacio y el token de autorización según RFC 6750. Desafortunadamente, hay casos donde se omite el esquema de autorización. Para tener en cuenta esta omisión al analizar, API Management divide el valor de encabezado en un espacio y selecciona la última cadena de la matriz de cadenas devuelta. Este método proporciona una solución alternativa para los encabezados de autorización con formato incorrecto.
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
Realización de la solicitud de validación
Una vez que API Management tiene el token de autorización, API Management puede realizar la solicitud para validarlo. RFC 7662 llama introspección a este proceso y requiere que establezca POST en un formulario HTML en el recurso de introspección. El formulario HTML debe contener al menos un par clave-valor con la clave token. Esta solicitud al servidor de autorización también debe autenticarse para asegurarse de que los clientes malintencionados no puedan rastrear tokens válidos.
<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>
Comprobación de la respuesta
El response-variable-name atributo se usa para conceder acceso a la respuesta devuelta. El nombre definido en esta propiedad se puede usar como clave en el diccionario context.Variables para acceder al objeto IResponse.
En el objeto de respuesta, puede recuperar el cuerpo, y RFC 7622 indica a API Management que la respuesta debe ser un objeto JSON que contenga al menos una propiedad denominada active, que es un valor booleano. Cuando active es true, el token se considera válido.
Como alternativa, si el servidor de autorización no incluye el "active" campo para indicar si el token es válido, use una herramienta de cliente HTTP como curl para determinar qué propiedades se establecen en un token válido. Por ejemplo, si una respuesta de token válida contiene una propiedad denominada "expires_in", compruebe si este nombre de propiedad existe en la respuesta del servidor de autorización de esta manera:
<when condition="@(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>().Property("expires_in") == null)">
Notificación de error
Puede usar una directiva <choose> para detectar si el token no es válido y, en caso de no serlo, devolver una respuesta 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>
Según RFC 6750, que describe cómo bearer se deben usar los tokens, API Management también devuelve un WWW-Authenticate encabezado con la respuesta 401. WWW-Authenticate está pensado para indicar a un cliente cómo crear una solicitud debidamente autorizada. Debido a la amplia variedad de enfoques posibles con el marco de OAuth2, es difícil comunicar toda la información necesaria. Por fortuna, se están adoptando medidas para enseñar a los clientes a autorizar debidamente las solicitudes a un servidor de recursos.
Solución final
Al final, obtendrá la siguiente directiva:
<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>
Este ejemplo es solo uno de los muchos que muestran cómo se puede usar la send-request directiva para integrar servicios externos útiles en el proceso de solicitudes y respuestas que fluyen a través del servicio API Management.
Composición de respuesta
La send-request directiva se puede usar para mejorar una solicitud principal a un sistema backend, como se vio en el ejemplo anterior, o se puede usar para reemplazar completamente la llamada al backend. Gracias a esta técnica, puede compilar fácilmente recursos compuestos que se agregan desde varios sistemas diferentes.
Creación de un panel
A veces le gustaría exponer información existente en varios sistemas de back-end, por ejemplo, para realizar un panel. Los indicadores clave de rendimiento (KPIs) proceden de todos los diversos back-ends, pero usted prefiere no proporcionar acceso directo a ellos. Sin embargo, sería bueno si toda la información se pudiera recuperar en una sola solicitud. Es posible que parte de la información de back-end deba segmentarse, desglosarse y corregirse un poco primero. Poder almacenar en caché ese recurso compuesto sería una manera útil de reducir la carga de back-end, ya que sabe que los usuarios tienen un hábito de golpear la clave F5 para ver si sus métricas con un rendimiento inferior pueden cambiar.
Emulación del recurso
El primer paso para crear el recurso del panel es configurar una nueva operación en Azure Portal. Esta operación de marcador de posición se usa para configurar una política de composición para crear el recurso dinámico.
Realización de las solicitudes
Una vez creada la operación, puede configurar una directiva específicamente para esa operación.
El primer paso consiste en extraer los parámetros de consulta de la solicitud entrante, de modo que pueda reenviarlos al back-end. En este ejemplo, el panel muestra información basada en un período de tiempo y, por tanto, tiene los parámetros fromDate y toDate. Puede usar la directiva set-variable para extraer la información de la dirección URL de la solicitud.
<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">
Una vez que tiene esta información, puede realizar solicitudes a todos los sistemas de back-end. Cada solicitud construye una nueva dirección URL con la información de los parámetros y llama a su servidor correspondiente y almacena la respuesta en una variable de contexto.
<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 envía estas solicitudes secuencialmente.
Respuesta
Para construir la respuesta compuesta, puede usar la directiva return-response. El elemento set-body puede usar una expresión para construir un nuevo elemento JObject con todas las representaciones de componentes insertadas como propiedades.
<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>
Este es el aspecto de la directiva completa:
<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>
Resumen
El servicio de administración de API de Azure proporciona directivas flexibles que se pueden aplicar de forma selectiva al tráfico HTTP y permite la composición de servicios de back-end. Si desea mejorar la puerta de enlace de la API con funciones de alerta, comprobación, capacidades de validación o crear nuevos recursos compuestos basados en varios servicios de back-end, la directiva send-request y otras relacionadas ofrecen todo un mundo de posibilidades.