Agrega compatibilidad con CORS a un proxy de API

Estás consultando la documentación de Apigee Edge.
Consulta 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 que se implementa de forma común para la “política del mismo origen” que todos los navegadores aplican. 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 a este problema, ya que permite que los servidores se “habiliten” si desean proporcionar uso compartido de recursos entre dominios.

Video: Mira un breve video para obtener información sobre cómo 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 (en este caso, un navegador) y la API de backend (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. Siempre y cuando el navegador sea compatible con CORS, estos encabezados le indicarán al navegador que puedes “flexibilizar” su política del mismo origen, lo que permite que la llamada a la API de origen cruzado se realice correctamente.

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 de Agregar CORS a un proxy de API nuevo

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 Agregar CORS se implementa como una política AssignMessage, que 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 o OAuth2) al encabezado Access-Control-Allow-Headers, como se muestra en el extracto de código que aparece a continuación.
  • Para la autenticación OAuth2, es posible que debas tomar medidas para corregir el comportamiento no compatible con RFC.
  • Se recomienda usar <Set> para configurar los encabezados de CORS en lugar de <Add>, como se muestra en el siguiente extracto. Cuando usas <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 varios 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 de forma manual una nueva política de asignación de mensajes y copiar en él el código de la política Agregar política de CORS que se indica en la sección anterior. Luego, adjunta la política al flujo previo de respuesta del TargetEndpoint del proxy de API. Puedes modificar los valores del encabezado según sea necesario. 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 de 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 Origin y Access-Control-Request-Method 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

Una solución de CORS de muestra, implementada como un flujo compartido, está disponible en GitHub. Importa el paquete de flujo compartido a tu entorno y adjúntalo mediante hooks de flujo o directamente a los flujos del proxy de API. Para obtener más información, consulta el archivo README de CORS-Shared-FLow proporcionado con la muestra.