Intentando usar fetch and pass en modo: no-cors

166

Puedo alcanzar este punto final, a http://catfacts-api.appspot.com/api/facts?number=99través de Postman y vuelveJSON

Además, estoy usando create-react-app y me gustaría evitar configurar cualquier configuración de servidor.

En mi código de cliente estoy tratando de usar fetchpara hacer lo mismo, pero aparece el error:

No hay encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Por lo tanto, el origen ' http: // localhost: 3000 ' no tiene acceso permitido. Si una respuesta opaca satisface sus necesidades, configure el modo de la solicitud en 'no-cors' para recuperar el recurso con CORS deshabilitado.

Así que estoy tratando de pasar un objeto a mi Fetch que deshabilitará CORS, así:

fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });

Curiosamente, el error que obtengo es en realidad un error de sintaxis con esta función. No estoy seguro de que mi real fetchesté roto, porque cuando elimino el objeto {mode: 'no-cors'} y le proporciono una URL diferente, funciona bien.

También intenté pasar el objeto { mode: 'opaque'}, pero esto devuelve el error original de arriba.

Creo que todo lo que necesito hacer es deshabilitar CORS. ¿Qué me estoy perdiendo?

dwww
fuente

Respuestas:

291

mode: 'no-cors'mágicamente no hará que las cosas funcionen. De hecho, empeora las cosas, porque un efecto que tiene es decirle a los navegadores: "Bloquee el código JavaScript de mi interfaz para que no vea el contenido del cuerpo de respuesta y los encabezados en todas las circunstancias". Por supuesto, casi nunca quieres eso.

Lo que sucede con las solicitudes de origen cruzado de JavaScript frontend es que los navegadores de forma predeterminada impiden que el código frontend acceda a los recursos de origen cruzado. Si Access-Control-Allow-Originhay una respuesta, los navegadores relajarán ese bloqueo y permitirán que su código acceda a la respuesta.

Pero si un sitio no envía Access-Control-Allow-Originsus respuestas, su código de interfaz no puede acceder directamente a las respuestas de ese sitio. En particular, no puede solucionarlo especificando mode: 'no-cors'(de hecho, eso asegurará que su código de interfaz no pueda acceder al contenido de la respuesta).

Sin embargo, una cosa que va a trabajar: si envía su solicitud a través de un proxy CORS , así:

var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
    targetUrl = 'http://catfacts-api.appspot.com/api/facts?number=99'
fetch(proxyUrl + targetUrl)
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    document.querySelector("pre").innerHTML = JSON.stringify(data, null, 2);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });
<pre></pre>

Nota: si cuando intenta utilizar https://cors-anywhere.herokuapp.com, encuentra que está inactivo , también puede implementar fácilmente su propio proxy en Heroku en literalmente solo 2-3 minutos, con 5 comandos:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

Después de ejecutar esos comandos, terminará con su propio servidor CORS Anywhere ejecutándose en, por ejemplo, https://cryptic-headland-94862.herokuapp.com/ . Entonces, en lugar de prefijar su URL de solicitud con https://cors-anywhere.herokuapp.com, prefije en su lugar con la URL de su propia instancia; por ejemplo, https://cryptic-headland-94862.herokuapp.com/https://example.com .


Puedo alcanzar este punto final, a http://catfacts-api.appspot.com/api/facts?number=99través de Postman

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explica por qué, aunque puede acceder a la respuesta con Postman, los navegadores no le permitirán acceder a la respuesta de origen cruzado desde la interfaz Código JavaScript que se ejecuta en una aplicación web a menos que la respuesta incluya un Access-Control-Allow-Originencabezado de respuesta.

http://catfacts-api.appspot.com/api/facts?number=99 no tiene Access-Control-Allow-Originencabezado de respuesta, por lo que no hay forma de que su código de interfaz pueda acceder al origen cruzado de la respuesta.

Su navegador puede obtener una buena respuesta y puede verla en Postman e incluso en las herramientas del navegador, pero eso no significa que los navegadores lo expongan a su código. No lo harán, porque no tiene Access-Control-Allow-Originencabezado de respuesta. Por lo tanto, debe usar un proxy para obtenerlo.

El proxy realiza la solicitud a ese sitio, obtiene la respuesta, agrega el Access-Control-Allow-Originencabezado de respuesta y cualquier otro encabezado CORS necesario, luego lo devuelve a su código de solicitud. Y esa respuesta con el Access-Control-Allow-Originencabezado agregado es lo que ve el navegador, por lo que permite que el código de la interfaz acceda realmente a la respuesta.


Así que estoy tratando de pasar un objeto a mi Fetch que deshabilitará CORS

No quieres hacer eso. Para ser claros, cuando dice que desea "deshabilitar CORS", parece que realmente quiere decir que desea deshabilitar la política del mismo origen . CORS en sí mismo es en realidad una forma de hacerlo: CORS es una forma de aflojar la política del mismo origen, no una forma de restringirla.

Pero de todos modos, es cierto que puede, solo en su entorno local, hacer cosas como dar banderas de tiempo de ejecución de su navegador para deshabilitar la seguridad y ejecutar de forma insegura, o puede instalar una extensión de navegador localmente para evitar la política del mismo origen, pero todo eso es cambiar la situación solo para usted localmente.

No importa lo que cambie localmente, cualquier otra persona que intente usar su aplicación seguirá cumpliendo con la política del mismo origen, y no hay forma de que pueda deshabilitarla para otros usuarios de su aplicación.

Lo más probable es que nunca quieras usarlo mode: 'no-cors'en la práctica, excepto en algunos casos limitados , e incluso si solo sabes exactamente lo que estás haciendo y cuáles son los efectos. Esto se debe a que lo que la configuración mode: 'no-cors'realmente le dice al navegador es: "Bloquee mi código JavaScript frontend para que no investigue el contenido del cuerpo de respuesta y los encabezados en todas las circunstancias". En la mayoría de los casos, eso obviamente no es lo que quieres.


En cuanto a los casos en los que podría desear considerar el uso mode: 'no-cors', véase la respuesta a ¿Qué limitaciones se aplican a las respuestas opacas? para los detalles La esencia de esto es que los casos son:

  • En el caso limitado cuando se está utilizando JavaScript para hacer un recurso de otro origen del contenido de una <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, o <iframe>elemento (que funciona porque la incorporación de los recursos de origen cruzado está permitido para aquellos) - pero por alguna razón no desea o no puede hacerlo simplemente haciendo que el marcado del documento use la URL del recurso como el atributo hrefo srcpara el elemento.

  • Cuando lo único que desea hacer con un recurso es almacenarlo en caché. Como se aludió en la respuesta en ¿Qué limitaciones se aplican a las respuestas opacas? , en la práctica, el escenario que se aplica es cuando usa Service Workers, en cuyo caso la API relevante es la API de almacenamiento en caché .

Pero incluso en esos casos limitados, hay algunas trampas importantes a tener en cuenta; vea la respuesta en ¿Qué limitaciones se aplican a las respuestas opacas? para los detalles


También he tratado de pasar el objeto { mode: 'opaque'}

No hay mode: 'opaque'modo de solicitud; en opaquecambio, es solo una propiedad de la respuesta , y los navegadores configuran esa propiedad opaca en las respuestas de las solicitudes enviadas con el no-corsmodo.

Pero, por cierto, la palabra opaco es una señal bastante explícita sobre la naturaleza de la respuesta con la que termina: "opaco" significa que no puede verla.

sidehowbarker
fuente
44
Me encanta la cors-anywheresolución para casos de uso simples no relacionados con productos (es decir, obtener algunos datos disponibles públicamente). Esta respuesta confirma mi sospecha de que no-corsno es común porque su respuesta opaca no es muy útil; es decir, "casos muy limitados"; ¿Alguien puede explicarme ejemplos donde no-corses útil?
The Red Pea
1
@TheRedPea Vea la actualización que hice a la respuesta aquí (y que también agregué como comentarios para su pregunta en stackoverflow.com/questions/52569895/… )
sideshowbarker
Necesitaba configurar mi servidor Express con el paquete CORS.js - github.com/expressjs/cors - y luego necesitaba eliminarlo mode: 'no-cors'de la solicitud de recuperación (de lo contrario, la respuesta estaría vacía)
James L.
El proxy CORS es genial. Me sorprende que se considere una medida de "seguridad" para bloquear el acceso a los archivos públicos. Es muy fácil moverse desde la perspectiva de un hacker, y eso es exactamente lo que hace. Realmente es una molestia para los buenos actores.
Seph Reed
Genero el código de diferentes clientes que consumen la misma API. Lo uso para entrenamientos. Quiero mantener el código simple y permitir que los usuarios jueguen con él. Necesito usar no-cros.
profimedica
6

Entonces, si eres como yo y estás desarrollando un sitio web en localhost donde estás tratando de obtener datos de la API de Laravel y usarlos en tu front-end de Vue, y ves este problema, así es como lo resolví:

  1. En su proyecto Laravel, ejecute el comando php artisan make:middleware Cors. Esto creará app/Http/Middleware/Cors.phppara ti.
  2. Agregue el siguiente código dentro de la handlesfunción en Cors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  3. En app/Http/kernel.php, agregue la siguiente entrada en la $routeMiddlewarematriz:

    cors => \App\Http\Middleware\Cors::class

    (Habría otras entradas en la matriz como auth, guestetc. Asegúrese también de que está haciendo esto app/Http/kernel.phpporque hay otra kernel.phptambién en Laravel)

  4. Agregue este middleware en el registro de ruta para todas las rutas donde desea permitir el acceso, de esta manera:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
  5. En el front-end de Vue, asegúrese de llamar a esta API en mounted()función y no en data(). También asegúrese de usar http://o https://con la URL en su fetch()llamada.

Créditos completos al artículo del blog de Pete Houston .

punto net
fuente
3

La solución simple: agregue lo siguiente a la parte superior del archivo php del que solicita los datos.

header("Access-Control-Allow-Origin: *");

Código realmente agradable
fuente
77
Esta es una muy buena idea si desea la menor cantidad de seguridad posible :).
Heretic Monkey
¿qué pasa con la imagen hotlink
user889030
1

La solución para mí fue simplemente hacerlo del lado del servidor

Usé la WebClientbiblioteca C # para obtener los datos (en mi caso, eran datos de imágenes) y enviarlos de vuelta al cliente. Probablemente haya algo muy similar en el idioma del lado del servidor elegido.

//Server side, api controller

[Route("api/ItemImage/GetItemImageFromURL")]
public IActionResult GetItemImageFromURL([FromQuery] string url)
{
    ItemImage image = new ItemImage();

    using(WebClient client = new WebClient()){

        image.Bytes = client.DownloadData(url);

        return Ok(image);
    }
}

Puede ajustarlo a lo que sea su propio caso de uso. El punto principal se client.DownloadData()trabaja sin ningún error CORS. Por lo general, los problemas de CORS son solo entre sitios web, por lo tanto, está bien hacer solicitudes 'entre sitios' desde su servidor.

Entonces, la llamada de recuperación de React es tan simple como:

//React component

fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {            
        method: 'GET',
    })
    .then(resp => resp.json() as Promise<ItemImage>)
    .then(imgResponse => {

       // Do more stuff....
    )}
Stuart Aitken
fuente
1

La solución muy fácil (2 minutos para configurar) es usar el paquete local-ssl-proxy denpm

El uso es bastante sencillo:
1. Instale el paquete: npm install -g local-ssl-proxy
2. Mientras ejecuta su local-servermáscara, con ellocal-ssl-proxy --source 9001 --target 9000

PD: reemplazar --target 9000con -- "number of your port"y --source 9001con--source "number of your port +1"

volna
fuente
1
Tengo problemas para usar mi aplicación en un dispositivo de teléfono real. ¿Es útil su solución para este caso?
Hamid Araghi
@HamidAraghi Supongo que ahora, ya que para fines de desarrollo, el servidor proxy debería ejecutarse en el dispositivo que realmente se ve afectado por el error
volna