Manejo de CORS con Node.js

En este artículo, configuraremos CORS con Express y Node.js y lo configuraremos para rutas únicas, todas las rutas y permitiremos la configuración dinámica.

Introducción

En este artículo, veremos qué es CORS, cómo puede configurar CORS con Express y cómo personalizar el middleware de CORS según sus necesidades.

¿Qué es CORS

CORS es la abreviatura de Intercambio de recursos de origen cruzado. Es un mecanismo para permitir o restringir los recursos solicitados en un servidor web dependiendo de dónde se inició la solicitud HTTP.

Esta política se utiliza para proteger un determinado servidor web del acceso de otro sitio web o dominio. Por ejemplo, solo los dominios permitidos podrán acceder a los archivos alojados en un servidor, como una hoja de estilo, una imagen o un script.

Si actualmente está en http://example.com/page1 y hace referencia a una imagen de http://image.com/myimage.jpg, no podrá obtener esa imagen a menos que http ://image.com permite compartir orígenes cruzados con http://example.com.

Hay un encabezado HTTP llamado origen en cada solicitud HTTP. Define desde dónde se ha originado la solicitud de dominio. Podemos usar la información del encabezado para restringir o permitir que los recursos de nuestro servidor web los protejan.

Por defecto, las solicitudes de cualquier otro origen estarán restringidas por el navegador.

Por ejemplo, mientras aún se encuentra en la etapa de desarrollo, si está utilizando una biblioteca de front-end como React, su aplicación de front-end se servirá en http://localhost:3000. Mientras tanto, su servidor Express podría estar ejecutándose en un puerto diferente, como http://localhost:2020.

Debido a esto, deberá permitir CORS entre esos servidores.

Si ve este error común en la consola de su navegador. Las restricciones de CORS podrían ser el problema:

chrome cors

CORS es realmente útil cuando ofrece una API pública y le gustaría controlar el acceso a ciertos recursos y cómo la gente los usa.

Además, si desea usar su propia API o archivos en una página web diferente, simplemente puede configurar CORS para permitir eso, mientras bloquea a otros.

Configuración de CORS con Express {#configuración de corswithexpress}

Comencemos con un nuevo proyecto. Haremos un directorio para él, lo ingresaremos y ejecutaremos npm init con la configuración predeterminada:

1
2
3
$ mkdir myapp
$ cd myapp
$ npm init -y

Luego instalemos los módulos requeridos. Usaremos express y el middleware cors:

1
2
$ npm i --save express
$ npm i --save cors

Luego, comencemos a crear una aplicación web rápida con dos rutas para demostrar cómo funciona CORS.

Crearemos un archivo, llamado index.js que actúa como un servidor web, con un par de controladores de solicitudes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const express = require('express');
const cors = require('cors');

const app = express();

app.get('/', (req, res) => {
    res.json({
        message: 'Hello World'
    });
});

app.get('/:name', (req, res) => {
    let name = req.params.name;

    res.json({
        message: `Hello ${name}`
    });
});

app.listen(2020, () => {
    console.log('server is listening on port 2020');
});

Ejecutemos la aplicación y el servidor:

1
$ node index.js

Ahora, si va a http://localhost:2020/, el servidor debería devolver un mensaje JSON:

1
2
3
{
  "message": "Hello World"
}

Alternativamente, si va a http://localhost:2020/something debería ver:

1
2
3
{
  "message": "Hello something"
}

Habilitar todas las solicitudes CORS

Si desea habilitar CORS para todas las solicitudes, simplemente puede usar el middleware cors antes de configurar sus rutas:

1
2
3
4
5
6
7
8
const express = require('express');
const cors = require('cors');

const app = express();

app.use(cors())

......

Esto permitirá acceder a todas las rutas desde cualquier lugar de la web si eso es lo que necesita. Entonces, en nuestro ejemplo, ambas rutas serán accesibles para todos los dominios.

Por ejemplo, si nuestro servidor se ejecuta en http://www.ejemplo.com y ofrece contenido como imágenes, permitimos que otros dominios, como http://www.dominiodiferente.com, hagan referencia al contenido de http://www.ejemplo.com.

Por lo tanto, una página web en http://www.diferentedominio.com puede usar nuestro dominio como fuente para una imagen:

1
<img src="http://www.example.com/img/cat.jpg">

Habilitar CORS para una sola ruta

Pero si necesita que una ruta determinada sea accesible y no otras rutas, puede configurar cors en una ruta determinada como un middleware en lugar de configurarlo para toda la aplicación:

1
2
3
4
5
app.get('/', cors(), (req, res) => {
    res.json({
        message: 'Hello World'
    });
});

Esto permitirá que una determinada ruta sea accesible por cualquier dominio. Entonces, en su caso, solo la ruta / será accesible para cada dominio. La ruta /:name solo será accesible para las solicitudes que se iniciaron en el mismo dominio que la API, que es http://localhost:2020 en nuestro caso.

Por ejemplo, si intenta enviar una solicitud de recuperación a la ruta / desde un origen diferente, tendrá éxito y obtendrá el mensaje Hello World como respuesta:

1
2
3
4
fetch('http://localhost:2020/')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(err => console.error(err));

Debería ver que la respuesta del servidor se registra correctamente en la consola si ejecuta este código:

1
2
3
{
    message: 'Hello World'
}

Pero si intenta acceder a cualquier otra ruta que no sea la ruta raíz, como http://localhost:2020/name o http://localhost:2020/img/cat.png, esta solicitud será bloqueada por el navegador:

1
2
3
4
fetch('http://localhost:2020/name/janith')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

Debería ver el siguiente error si intenta ejecutar este código en una aplicación web diferente:

console error

Configurar CORS con opciones

También puede usar las opciones de configuración con CORS para personalizar esto aún más. Puede usar la configuración para permitir el acceso a un solo dominio o subdominios, configure los métodos HTTP que están permitidos, como “GET” y “POST”, según sus requisitos.

Así es como puede permitir el acceso a un solo dominio usando las opciones de CORS:

1
2
3
4
5
6
var corsOptions = {
    origin: 'http://localhost:8080',
    optionsSuccessStatus: 200 // For legacy browser support
}

app.use(cors(corsOptions));

Si configura el nombre de dominio en el origen, el servidor permitirá CORS desde el dominio configurado. Entonces la API será accesible desde http://localhost:8080 en nuestro caso y bloqueada para otros dominios.

Si enviamos una solicitud GET, el acceso a cualquier ruta debería funcionar, ya que las opciones se aplican a nivel de aplicación, no a nivel de función.

Entonces, si ejecutamos el siguiente código y enviamos una solicitud desde http://localhost:8080 a http://localhost:2020:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fetch('http://localhost:2020/')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

// Or

fetch('http://localhost:2020/name/janith')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

Estamos autorizados a obtener la información de esa aplicación y dominio.

También puede configurar los métodos HTTP permitidos si desea:

1
2
3
4
5
6
7
var corsOptions = {
    origin: 'http://localhost:8080',
    optionsSuccessStatus: 200 // For legacy browser support
    methods: "GET, PUT"
}

app.use(cors(corsOptions));

Si enviamos una solicitud POST desde http://localhost:8080, el navegador la bloqueará ya que solo se admiten GET y PUT:

1
2
3
4
5
6
7
fetch('http://localhost:2020', {
  method: 'POST',
  body: JSON.stringify({name: "janith"}),
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));

Para ver una lista completa de opciones de configuración, consulte la documentación oficial.

Configuración de orígenes de CORS dinámicos mediante una función {#configuración de orígenes de CORS dinámicos mediante una función}

Si las configuraciones no satisfacen sus requisitos, puede crear su función para personalizar CORS.

Por ejemplo, supongamos que desea permitir el uso compartido de CORS para archivos .jpg http://something.com y http://example.com:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const allowlist = ['http://something.com', 'http://example.com'];

    const corsOptionsDelegate = (req, callback) => {
    let corsOptions;

    let isDomainAllowed = whitelist.indexOf(req.header('Origin')) !== -1;
    let isExtensionAllowed = req.path.endsWith('.jpg');

    if (isDomainAllowed && isExtensionAllowed) {
        // Enable CORS for this request
        corsOptions = { origin: true }
    } else {
        // Disable CORS for this request
        corsOptions = { origin: false }
    }
    callback(null, corsOptions)
}

app.use(cors(corsOptionsDelegate));

La función de devolución de llamada aceptará dos parámetros. El primero es un error donde pasamos null y el segundo son opciones donde pasamos { origen: falso }. El segundo parámetro podría ser muchas opciones que se construyen usando el objeto request del manejador de solicitudes Express.

Entonces, una aplicación web que está alojada en http://something.com o http://example.com podría referir una imagen con la extensión .jpg desde el servidor como lo hemos configurado en nuestro personalizado función.

Entonces, el siguiente archivo adjunto de imagen será exitoso desde cualquiera de estos:

1
<img src="http://yourdomain.com/img/cat.jpg">

Pero el siguiente adjunto será bloqueado:

1
<img src="http://yourdomain.com/img/cat.png">

Lista de carga de orígenes permitidos desde una fuente de datos

También puede usar una lista de dominios permitidos de una base de datos o usar cualquier fuente de datos de respaldo para permitir CORS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var corsOptions = {
    origin: function (origin, callback) {
        // Loading a list of allowed origins from the database
        // Ex.. origins = ['http://example.com', 'http//something.com']
        database.loadOrigins((error, origins) => {
            callback(error, origins);
        });
    }
}

app.use(cors(corsOptions));

Conclusión

En este artículo, hemos cubierto qué es CORS y cómo puede configurarlo con Express. Luego, configuramos CORS para todas las solicitudes, para solicitudes específicas, agregamos opciones y restricciones, y definimos una función personalizada para la configuración dinámica de CORS.