Agrega compatibilidad con CORS a un proxy de API

Estás viendo la documentación de Apigee Edge.
Ve a la Documentación de Apigee X.
información

CORS (uso compartido de recursos entre dominios) es un mecanismo estándar que permite que las llamadas XMLHttpRequest (XHR) de JavaScript que se ejecutan en una página web interactúen con recursos de dominios que no son de origen. CORS es una solución de implementación frecuente para la “política del mismo origen” que aplica de manera forzosa todos los navegadores. Por ejemplo, si realizas una llamada XHR a la API de Twitter desde el código JavaScript que se ejecuta en el navegador, la llamada fallará. Esto se debe a que el dominio que entrega la página a tu navegador no es el mismo dominio que entrega la API de Twitter. CORS proporciona una solución para este problema al permitir que los servidores acepten si quieren proporcionar recursos de origen cruzado para compartir.

Video: Mira un video breve para aprender a habilitar CORS en un proxy de API.

Caso de uso típico para CORS

El siguiente código de JQuery llama a un servicio de destino ficticio. Si se ejecuta desde el contexto de un navegador (una página web), la llamada fallará debido a la política de mismo origen:

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

Una solución a este problema es crear un proxy de API de Apigee que llame a la API de servicio en el backend. Recuerda que Edge se encuentra entre el cliente (un navegador en este caso) y el backend. API (el servicio). Debido a que el proxy de API se ejecuta en el servidor, no en un navegador, puede llamar al servicio de forma correcta. Luego, todo lo que necesitas hacer es adjuntar encabezados de CORS a la respuesta TargetEndpoint. Si el navegador es compatible con CORS, estos encabezados le indican al navegador que está bien "relajarse" su política del mismo origen, lo que permite la llamada a la API de origen cruzado tenga éxito.

Una vez que se crea el proxy con la compatibilidad con CORS, puedes llamar a la URL del proxy de la API en lugar del servicio de backend en tu código del cliente. Por ejemplo:

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

Adjunta una política agregar CORS a un nuevo proxy de API

Puedes agregar compatibilidad con CORS a un proxy de API si adjuntas una política "Agregar CORS" al proxy de API cuando la creas. Para agregar esta política, selecciona la casilla de verificación Agregar encabezados CORS en la página de seguridad del asistente de compilación de un proxy.

Cuando seleccionas esta casilla de verificación, una política llamada Agregar CORS se agrega de manera automática al sistema y se adjunta al flujo previo de respuestas de TargetEndpoint, como se muestra en la siguiente figura:

Se agregó la política Agregar CORS al navegador en Políticas y se adjuntó al flujo previo de la respuesta de TargetEndpoint en el desplazamiento lateral derecho

La política Add CORS se implementa como una política AssignMessage, agrega los encabezados adecuados a la respuesta. Básicamente, los encabezados informan al navegador de los orígenes con los que compartirá sus recursos, qué métodos acepta, etc. Puedes obtener más información sobre estos encabezados de CORS en la Recomendación de uso compartido de recursos entre dominios de W3C.

Debes modificar la política de la siguiente manera:

  • Agrega los encabezados content-type y authorization (obligatorios para admitir la autenticación básica u OAuth2) al encabezado Access-Control-Allow-Headers, como se muestra en el siguiente extracto de código.
  • Para la autenticación OAuth2, es posible que debas tomar medidas para corregir el comportamiento no compatible con RFC.
  • Se recomienda que uses <Set> para configurar los encabezados de CORS en lugar de <Add>, como se muestra en el siguiente extracto. Cuando uses <Add>, si el encabezado Access-Control-Allow-Origin ya existe, recibirás el siguiente error:

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    Para obtener más información, consulta Error de CORS : El encabezado contiene múltiples valores “*, *”, pero solo se permite uno.

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Agrega encabezados CORS a un proxy existente

Debes crear manualmente una nueva política de asignación de mensajes y copiar el código para Add CORS política mencionada en la sección anterior. Luego, adjunta la política al flujo previo de respuesta de el TargetEndpoint del proxy de API. Puedes modificar los valores del encabezado según sea necesario. Para ver más para obtener más información sobre cómo crear y adjuntar políticas, consulta ¿Qué es una política?

Maneja las solicitudes de comprobación previa de CORS

La comprobación previa de CORS hace referencia al envío de una solicitud a un servidor para que verifique si es compatible con CORS. Las respuestas de comprobación previa típicas incluyen de qué orígenes el servidor acepta las solicitudes CORS, una lista de métodos HTTP compatibles con solicitudes CORS, encabezados que se pueden usar como parte de la solicitud de recursos, el tiempo máximo que la respuesta de comprobación previa se almacena en caché y otros. Si el servicio no indica la compatibilidad con CORS o no desea aceptar solicitudes de origen cruzado desde el origen del cliente, se aplicará la política de origen cruzado del navegador y fallará cualquier solicitud de dominio cruzado que haga el cliente para interactuar con los recursos alojados en ese servidor.

Por lo general, las solicitudes preliminares de CORS se realizan con el método OPTIONS DE HTTP. Cuando un servidor que admite CORS recibe una solicitud de OPTIONS, muestra un conjunto de encabezados de CORS al cliente que indica su nivel de compatibilidad con CORS. Como resultado de este protocolo de enlace, el cliente sabe qué se le permite solicitar desde el dominio que no es de origen.

Para obtener más información sobre comprobación previa, consulta la Recomendación del uso compartido de recursos entre dominios W3C. Hay varios blogs y artículos sobre CORS que puedes consultar.

Apigee no incluye una solución preliminar de CORS lista para usar, pero es posible implementarla, como se describe en esta sección. El objetivo es que el proxy evalúe una solicitud de OPTIONS en un flujo condicional. Luego, puede enviar una respuesta adecuada al cliente.

Analicemos un flujo de muestra y, luego, veamos las partes que manejan la solicitud de comprobación previa:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

Las partes clave de este ProxyEndpoint son las siguientes:

  • Se crea una RouteRule en un objetivo NULL con una condición para la solicitud de OPTIONS. Ten en cuenta que no hay ningún TargetEndpoint especificado. Si se recibe la solicitud de OPTIONS, y los encabezados de la solicitud de origen y Access-Control-Request-Method no son nulos, el proxy muestra de inmediato los encabezados de CORS en una respuesta al cliente (si se omite el destino “backend” predeterminado). Para obtener detalles sobre las condiciones de flujo y RouteRule, consulta Condiciones con variables de flujo.

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    
  • Se crea un flujo OptionsPreFlight que agrega una política Add CORS, que contiene los encabezados CORS, al flujo si se recibe una solicitud OPTIONS y los encabezados de solicitud de origen y acceso-Control-Request no son nulos.

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>
    

Usa la solución de CORS de muestra

Hay una solución de CORS de muestra, implementada como un flujo compartido, disponible en GitHub. Importa el paquete de flujo compartido a tu entorno y conéctalo mediante hooks de flujo o directamente a los flujos del proxy de API. Para obtener más información, consulta la El archivo CORS-Shared-FLow README se proporciona con la muestra.