No hay un encabezado "Acceso-Control-Autorización-Origen" en el recurso solicitado cuando se trata de obtener datos de un REST API

javascript api cors fetch-api preflight


Estoy tratando de obtener algunos datos de la API REST de HP Alm.Funciona bastante bien con un pequeño script de rizos-obtengo mis datos.

Ahora,hacer eso con JavaScript,fetch y ES6 (más o menos)parece ser un problema mayor.Sigo recibiendo este mensaje de error:

Fetch API no se puede cargar. La respuesta a la solicitud de verificación previa no pasa la verificación de control de acceso: no hay encabezado 'Access-Control-Allow-Origin' en el recurso solicitado. Por lo tanto, el origen ' http://127.0.0.1:3000 ' no tiene acceso permitido. La respuesta tenía el código de estado HTTP 501. Si una respuesta opaca satisface sus necesidades, configure el modo de la solicitud en 'no-cors' para recuperar el recurso con CORS deshabilitado.

Entiendo que esto es porque estoy tratando de obtener esos datos desde mi host local y la solución debería usar CORS.Ahora,pensé que en realidad hice eso,pero de alguna manera o bien ignora lo que escribo en el encabezado o el problema es otra cosa?

Entonces,¿hay algún problema de aplicación? ¿Lo estoy haciendo mal? No puedo comprobar los registros del servidor,por desgracia.Estoy realmente un poco atascado aquí.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

Estoy usando Chrome.También intenté usar el plugin CORS de Chrome,pero me aparece otro mensaje de error:

El valor del encabezado 'Access-Control-Allow-Origin' en la respuesta no debe ser el comodín '*' cuando el modo de credenciales de la solicitud es 'incluir'. Por lo tanto, el origen ' http://127.0.0.1:3000 ' no tiene acceso permitido. El modo de credenciales de las solicitudes iniciadas por XMLHttpRequest está controlado por el atributo withCredentials.




Answer 1 sideshowbarker


Esta respuesta cubre mucho terreno, por lo que se divide en tres partes:

  • Cómo usar un proxy CORS para solucionar los problemas de "Sin encabezado Access-Control-Allow-Origin"
  • Cómo evitar el pre-vuelo del CORS
  • Cómo corregir “encabezado Access-Control-Allow-Origen no debe ser el comodín” problemas

Cómo usar un proxy CORS para solucionar los problemas de "Sin encabezado Access-Control-Allow-Origin"

Si no controla el servidor al que su código JavaScript frontend envía una solicitud, y el problema con la respuesta de ese servidor es solo la falta del encabezado necesario Access-Control-Allow-Origin , aún puede hacer que las cosas funcionen : Haciendo la solicitud a través de un proxy CORS. Para mostrar cómo funciona, primero hay un código que no usa un proxy CORS:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

La razón por la cual el bloque catch se ve afectado es que el navegador evita que ese código acceda a la respuesta que regresa de https://example.com . Y la razón por la que el navegador hace eso es que la respuesta carece del encabezado de respuesta Access-Control-Allow-Origin .

Ahora, este es exactamente el mismo ejemplo, pero solo con un proxy CORS agregado en:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Nota: Si https://cors-anywhere.herokuapp.com está caído o no está disponible cuando lo prueba, vea a continuación cómo implementar su propio servidor CORS Anywhere en Heroku en solo 2-3 minutos.

El segundo fragmento de código anterior puede acceder a la respuesta con éxito porque tomar la URL de la solicitud y cambiarla a https://cors-anywhere.herokuapp.com/https://example.com, simplemente con el prefijo de la URL del proxy, provoca solicitar que se realice a través de ese proxy, que luego:

  1. Reenvía la solicitud a https://example.com .
  2. Recibe la respuesta de https://example.com .
  3. Agrega el encabezado Access-Control-Allow-Origin a la respuesta.
  4. Pasa esa respuesta,con ese encabezado añadido,de vuelta al código de la interfaz solicitada.

Luego, el navegador permite que el código frontend acceda a la respuesta, porque esa respuesta con el encabezado de respuesta Access-Control-Allow-Origin es lo que ve el navegador.

Puede ejecutar fácilmente su propio proxy utilizando el código de https://github.com/Rob--W/cors-anywhere/ .
También puedes desplegar fácilmente tu propio proxy a Heroku en literalmente sólo 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 .

Entonces, si intenta utilizar https://cors-anywhere.herokuapp.com, encuentra que está inactivo (que a veces lo estará), considere obtener una cuenta de Heroku (si aún no lo hizo) y tome 2 o 3 minutos para realizar los pasos anteriores para implementar su propio servidor CORS Anywhere en Heroku.

Independientemente, ya sea que ejecute el suyo propio o use https://cors-anywhere.herokuapp.com u otro proxy abierto, esta solución funciona incluso si la solicitud es una que activa los navegadores para hacer una solicitud de OPTIONS verificación previa de CORS , porque en ese caso, el proxy también devuelve los Access-Control-Allow-Headers y Access-Control-Allow-Methods necesarios para que la verificación previa sea exitosa.


Cómo evitar la verificación previa CORS

El código en la pregunta desencadena una verificación previa de CORS, ya que envía un encabezado de Authorization .

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

Incluso sin eso, el Content-Type: application/json también desencadenaría la verificación previa.

Qué significa "verificación previa": antes de que el navegador intente la POST en el código de la pregunta, primero enviará una solicitud de OPTIONS al servidor, para determinar si el servidor está optando por recibir una POST de origen cruzado que incluye Authorization y tipo de Content-Type: application/json encabezados de aplicación / json .

Funciona bastante bien con una escritura de rizos pequeños-obtengo mis datos.

Para probar correctamente con curl , debe emular la solicitud de OPTIONS verificación previa que envía el navegador:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

... con https://the.sign_in.url reemplazado por cualquiera que sea su URL de sign_in real .

La respuesta que el navegador necesita ver de esa solicitud de OPTIONS debe incluir encabezados como este:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

Si la respuesta de OPTIONS no incluye esos encabezados, el navegador se detendrá allí y ni siquiera intentará enviar la solicitud POST . Además, el código de estado HTTP para la respuesta debe ser un 2xx, generalmente 200 o 204. Si se trata de cualquier otro código de estado, el navegador se detendrá allí.

El servidor en la pregunta está respondiendo a la solicitud de OPTIONS con un código de estado 501, lo que aparentemente significa que está tratando de indicar que no implementa soporte para solicitudes de OPTIONS . Otros servidores suelen responder con un código de estado 405 "Método no permitido" en este caso.

Por lo tanto, nunca podrá realizar solicitudes POST directamente a ese servidor desde su código JavaScript frontend si el servidor responde a esa solicitud OPTIONS con un 405 o 501 o cualquier otra cosa que no sea 200 o 204 o si no responde con esos encabezados de respuesta necesarios.

La manera de evitar que se desencadene un vuelo previo para el caso en cuestión sería:

  • si el servidor no requirió un encabezado de solicitud de Authorization sino que (por ejemplo) se basó en datos de autenticación incrustados en el cuerpo de la solicitud POST o como un parámetro de consulta
  • si el servidor no requirió que el cuerpo POST tuviera un tipo de Content-Type: application/json , sino que aceptó el cuerpo POST como application/x-www-form-urlencoded con un parámetro llamado json (o lo que sea) cuyo valor es los datos JSON

Cómo corregir “encabezado Access-Control-Allow-Origen no debe ser el comodín” problemas

Estoy recibiendo otro mensaje de error:

El valor del encabezado 'Access-Control-Allow-Origin' en la respuesta no debe ser el comodín '*' cuando el modo de credenciales de la solicitud es 'incluir'. Por lo tanto, el origen ' http://127.0.0.1:3000 ' no tiene acceso permitido. El modo de credenciales de las solicitudes iniciadas por XMLHttpRequest está controlado por el atributo withCredentials.

Para una solicitud que incluya credenciales, los navegadores no permitirán que su código JavaScript frontend acceda a la respuesta si el valor del encabezado de respuesta Access-Control-Allow-Origin es * . En cambio, el valor en ese caso debe coincidir exactamente con el origen de su código de interfaz, http://127.0.0.1:3000 .

Consulte Solicitudes con credenciales y comodines en el artículo de control de acceso HTTP (CORS) de MDN.

Si controla el servidor al que envía la solicitud, entonces una forma común de tratar este caso es configurar el servidor para que tome el valor del encabezado de la solicitud de Origin y haga eco / refleje de nuevo en el valor de Access-Control-Allow-Origin Cabecera de respuesta Control-Permitir-Origen . Por ejemplo, con nginx:

add_header Access-Control-Allow-Origin $http_origin

Pero ese es solo un ejemplo; otros sistemas de servidor (web) proporcionan formas similares de hacer eco de los valores de origen.


Estoy usando Chrome.También intenté usar el plugin CORS de Chrome

Ese complemento Chrome CORS aparentemente simplemente inyecta un encabezado Access-Control-Allow-Origin: * en la respuesta que ve el navegador. Si el complemento fuera más inteligente, lo que estaría haciendo es establecer el valor de ese falso encabezado de respuesta Access-Control-Allow-Origin al origen real de su código JavaScript frontend, http://127.0.0.1:3000 .

Por lo tanto, evite usar ese complemento, incluso para las pruebas. Es solo una distracción. Si desea probar qué respuestas obtiene del servidor sin que el navegador las filtre, es mejor que use curl -H como se indicó anteriormente.


En cuanto al código JavaScript frontend para la solicitud fetch(…) en la pregunta:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Elimina esas líneas. Los encabezados Access-Control-Allow-* son encabezados de respuesta . Nunca desea enviarlos en una solicitud. El único efecto que tendrá es activar un navegador para hacer una verificación previa.




Answer 2 Rakesh


Este error se produce cuando la URL del cliente y la del servidor no coinciden,incluido el número de puerto.En este caso necesitas habilitar tu servicio para el CORS que es compartir recursos de origen cruzado.

Si está alojando un servicio Spring REST, puede encontrarlo en la publicación de blog de soporte CORS en Spring Framework .

Si estás alojando un servicio usando un servidor Node.js entonces

  1. Detén el servidor Node.js.
  2. npm install cors --save
  3. Añade las siguientes líneas a tu server.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration



Answer 3 Lex Soft


El problema surgió porque agregó el siguiente código como encabezado de solicitud en su front-end:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Esos encabezados pertenecen a la respuesta , no a la solicitud. Entonces quítelos , incluida la línea:

headers.append('GET', 'POST', 'OPTIONS');

Su solicitud tenía 'Content-Type: application / json', por lo tanto, activó lo que se llama verificación previa CORS. Esto provocó que el navegador enviara la solicitud con el método OPTIONS. Consulte la verificación previa de CORS para obtener información detallada.
Por lo tanto, en su back-end , debe manejar esta solicitud con verificación previa devolviendo los encabezados de respuesta que incluyen:

Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept

Por supuesto,la sintaxis real depende del lenguaje de programación que uses para tu back-end.

En tu frente,debería ser así:

function performSignIn() {
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
    headers.append('Origin','http://localhost:3000');

    fetch(sign_in, {
        mode: 'cors',
        credentials: 'include',
        method: 'POST',
        headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}



Answer 4 Harrison O


En mi caso,utilizo la siguiente solución

Frontal o angular

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

back-end (uso php)

header("Access-Control-Allow-Origin: http://localhost:4200");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header("Access-Control-Allow-Headers: Content-Type, Authorization");

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
print_r($request);



Answer 5 Yair Levy


Solo mis dos centavos ... con respecto a cómo usar un proxy CORS para solucionar los problemas de "Sin Access-Control-Allow-Origin encabezado"

Para aquellos de ustedes que trabajan con php en el backend,desplegar un "proxy CORS" es tan simple como:

  1. crear un archivo llamado 'no-cors.php' con el siguiente contenido:

    $URL = $_GET['url']; echo json_encode(file_get_contents($URL)); die();

  2. en tu parte delantera,haz algo como:

    fetch('https://example.com/no-cors.php' + '?url=' + url) .then(response=>{*/Handle Response/*})