Estoy tratando de obtener algunos datos de la API REST de HP Alm. Funciona bastante bien con un pequeño script curl - Obtengo mis datos.
Ahora haciendo eso con JavaScript, fetch y ES6 (más o menos) parece ser un problema mayor. Sigo recibiendo este mensaje de error:
Fetch API no puede cargar
. La respuesta a la solicitud de verificación previa no pasar la comprobación de control de acceso: No 'Access-Control-Allow-Origin' header is presente en el recurso solicitado. El origen 'http://127.0.0.1:3000' es por lo tanto, no se permite el acceso. La respuesta tiene el código de estado HTTP 501. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud'a 'no-cors' para obtener el recurso con CORS desactivado.
Entiendo que esto se debe a que estoy tratando de obtener esos datos desde mi localhost y la solución debería ser usar CORS. Ahora bien, pensé que había hecho eso, pero de alguna manera o bien ignora lo que escribo en la cabecera o el problema es otro.
Entonces, ¿hay un problema de implementación? ¿Lo estoy haciendo mal? Lamentablemente, no puedo comprobar los registros del servidor. Realmente estoy 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 ese Plugin CORS de Chrome, pero entonces estoy recibiendo otro mensaje de error:
El valor de la cabecera Access-Control-Allow-Origin en la respuesta
no debe ser el comodín '*' cuando el modo de credenciales de la solicitud' es 'incluir'. El origen 'http://127.0.0.1:3000' no está por tanto permitido acceso. El modo de credenciales de las peticiones iniciadas por el XMLHttpRequest está controlado por el atributo withCredentials.
Esta respuesta cubre mucho terreno, por lo que se divide en tres partes:
*Cómo usar un proxy CORS para evitar los problemas de la cabecera "Access-Control-Allow-Origin "*.
Si no controlas el servidor al que tu código JavaScript está enviando una petición, y el problema con la respuesta de ese servidor es sólo la falta de la necesaria cabecera Access-Control-Allow-Origin
, todavía puedes hacer que las cosas funcionen, haciendo la petición a través de un proxy CORS. Para mostrar cómo funciona, primero hay un código que no utiliza un proxy CORS:
begin snippet: js hide: false console: true babel: false -->
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 que el bloque catch
es golpeado allí es que el navegador impide que ese código acceda a la respuesta que viene de vuelta de https://example.com
. Y la razón por la que el navegador hace eso es que la respuesta carece de la cabecera de respuesta Access-Control-Allow-Origin
.
Ahora, aquí está exactamente el mismo ejemplo pero con un proxy CORS añadido:
begin snippet: js hide: false console: true babel: false -->
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
https://example.com
.https://example.com
.Access-Control-Allow-Origin
a la respuesta.Access-Control-Allow-Origin
es lo que ve el navegador.
Puedes ejecutar fácilmente tu propio proxy utilizando el código de https://github.com/Rob--W/cors-anywhere/.git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master
https://cors-anywhere.herokuapp.com
, prefiérela con la URL de su propia instancia; por ejemplo, https://cryptic-headland-94862.herokuapp.com/https://example.com.
Así que si cuando vas a tratar de usar httpsOPTIONS
- porque en ese caso, el proxy también envía de vuelta las cabeceras Access-Control-Allow-Headers
y Access-Control-Allow-Methods
necesarias para hacer el preflight con éxito.Cómo evitar el preflight de CORS
El código de la pregunta desencadena un preflight CORS, ya que envía una cabecera Authorization
.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
Incluso sin eso, la cabecera Content-Type: application/json
también activaría el preflight.
Lo que significa "preflight": antes de que el navegador intente el POST
en el código de la pregunta, primero enviará una solicitud OPTIONS
al servidor - para determinar si el servidor está optando por recibir un POST
de origen cruzado que incluya las cabeceras Authorization
y Content-Type: application/json
.
Funciona bastante bien con un pequeño script curl - obtengo mis datos.
Para probar correctamente con curl
, es necesario emular la solicitud de preflight OPTIONS
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
sustituido por la URL real de sign_in
.
La respuesta que el navegador necesita ver de esa solicitud OPTIONS
debe incluir cabeceras como esta:
idioma: lang-none -->
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 OPTIONS
no incluye esas cabeceras, el navegador se detendrá ahí mismo y ni siquiera intentará enviar la petición POST
. Además, el código de estado HTTP de la respuesta debe ser un 2xx -típicamente 200 o 204-. Si es cualquier otro código de estado, el navegador se detendrá ahí mismo.
El servidor en cuestión está respondiendo a la petición OPTIONS
con un código de estado 501, lo que aparentemente significa que está tratando de indicar que no implementa soporte para peticiones OPTIONS
. Otros servidores suelen responder con un código de estado 405 "Método no permitido" en este caso.
Así que nunca vas a poder hacer peticiones POST
directamente a ese servidor desde tu código JavaScript del frontend si el servidor responde a esa petición OPTIONS
con un 405 o 501 o cualquier otra cosa que no sea un 200 o 204 o si no responde con esas cabeceras de respuesta necesarias.
La forma de evitar la activación de un preflight para el caso de la pregunta sería
Authorization
, sino que (por ejemplo) se basa en los datos de autenticación incrustados en el cuerpo de la solicitud POST
o como un parámetro de consulta.POST
tuviera un tipo de medio Content-Type: application/json
, sino que aceptara el cuerpo de la petición POST
como application/x-www-form-urlencoded
con un parámetro llamado json
(o lo que sea) cuyo valor son los datos JSON**Cómo arreglar los problemas de "Access-Control-Allow-Origin header must not be the wildcard".
Estoy recibiendo otro mensaje de error:
El valor de la cabecera '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, no se permite el origen 'http://127.0.0.1:3000' acceso. El modo de credenciales de las peticiones iniciadas por el XMLHttpRequest está controlado por el atributo withCredentials. Para una solicitud que incluya credenciales, los navegadores no permitirán que el código JavaScript del frontend acceda a la respuesta si el valor de la cabecera de respuesta
Access-Control-Allow-Origin
es `. En su lugar, el valor en ese caso debe coincidir exactamente con el origen de su código frontend,
http://127.0.0.1:3000. Vea [*Solicitudes acreditadas y comodines*][1] en el artículo de MDN sobre control de acceso HTTP (CORS). Si controlas el servidor al que envías la petición, una forma común de tratar este caso es configurar el servidor para que tome el valor de la cabecera de petición
Origin, y lo refleje en el valor de la cabecera de respuesta
Access-Control-Allow-Origin`. Por ejemplo, con nginx: idioma: lang-none -->
add_header Access-Control-Allow-Origin $http_origin
Estoy usando Chrome. También intenté usar ese plugin CORS de Chrome Ese plugin CORS de Chrome aparentemente sólo inyecta una cabecera
Access-Control-Allow-Origin: *
en la respuesta que ve el navegador. Si el plugin fuera más inteligente, lo que estaría haciendo es establecer el valor de ese falso encabezado de respuestaAccess-Control-Allow-Origin
al origen real de tu código JavaScript del frontend,http://127.0.0.1:3000
. Así que evita usar ese plugin, incluso para probarlo. Es sólo una distracción. Si quieres probar qué respuestas obtienes del servidor sin que el navegador las filtre, es mejor que utilicescurl -H
como se indica arriba.En cuanto al código JavaScript del frontend para la petición
fetch(...)
en la pregunta:
headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');
Elimine esas líneas. Las cabeceras Access-Control-Allow-*
son cabeceras de respuesta. Nunca querrá enviarlas en una petición. El único efecto que tendrá es provocar que el navegador haga una comprobación previa.
Este error se produce cuando la URL del cliente y la del servidor no coinciden, incluyendo el número de puerto. En este caso, debe habilitar su servicio para CORS, que es la compartición de recursos entre orígenes.
Si estás alojando un servicio REST de Spring, puedes encontrarlo en la entrada del blog Soporte de CORS en Spring Framework.
Si estás alojando un servicio usando un servidor Node.js entonces
Detenga el servidor Node.js.
npm install cors --save
Añade las siguientes líneas a tu server.js
var cors = require('cors')
app.use(cors()) // Utiliza esto después de la declaración de la variable