L'en-tête "Access-Control-Allow-Origin" n'est pas présent sur la ressource demandée lorsque vous essayez d'obtenir des données à partir d'une API REST.

J&#8217essaie d&#8217obtenir des données à partir de l&#8217API REST de HP Alm. Cela fonctionne assez bien avec un petit script curl - j'obtiens mes données.

Maintenant, faire cela avec JavaScript, fetch et ES6 (plus ou moins) semble être un plus gros problème. Je continue à obtenir ce message d'erreur :

Fetch API cannot load . La réponse à la demande de contrôle préalable ne passe pas le contrôle d'accès. passe pas le contrôle d'accès : Aucun en-tête 'Access-Control-Allow-Origin&#39 ; n'est présent sur la ressource demandée. présent sur la ressource demandée. L'origine 'http://127.0.0.1:3000&#39 ; est par conséquent, l'accès n'est pas autorisé. La réponse a le code d'état HTTP 501. Si une réponse opaque répond à vos besoins, réglez le mode de la demande sur 'no-cors&#39 ; pour récupérer la ressource avec CORS désactivé.

Je comprends que c'est parce que j'essaie de récupérer ces données à partir de mon hôte local et que la solution devrait être d'utiliser CORS. Je pensais l'avoir fait, mais soit il ignore ce que j'ai écrit dans l'en-tête, soit le problème est autre ?

Alors, y a-t-il un problème d'implémentation ? Est-ce que je m'y prends mal ? Je ne peux malheureusement pas vérifier les journaux du serveur. Je suis vraiment un peu coincé ici.

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));
}

J'utilise Chrome. J'ai également essayé d'utiliser le plugin CORS de Chrome, mais j'obtiens un autre message d'erreur :

La valeur de l'en-tête 'Access-Control-Allow-Origin&#39 ; dans la réponse ne doit pas être le caractère générique '*&#39 ; lorsque le mode d'authentification de la demande est 'include&#39 ;. L'origine 'http://127.0.0.1:3000&#39 ; n'est donc pas autorisée l'accès. Le mode d'informations d'identification des demandes initiées par la fonction XMLHttpRequest est contrôlé par l'attribut withCredentials.

Solution

Cette réponse couvre beaucoup de terrain, elle est donc divisée en trois parties :

  • Comment utiliser un proxy CORS pour contourner les problèmes de "No Access-Control-Allow-Origin header ".
  • Comment éviter le contrôle préalable CORS ?
  • Comment résoudre les problèmes de "Access-Control-Allow-Origin header must not be the wildcard ".

    *Comment utiliser un proxy CORS pour contourner les problèmes de "No Access-Control-Allow-Origin header "* ? Si vous ne contrôlez pas le serveur auquel votre code JavaScript frontal envoie une requête, et que le problème avec la réponse de ce serveur est juste l'absence de l'en-tête Access-Control-Allow-Origin nécessaire, vous pouvez toujours faire fonctionner les choses en faisant la requête à travers un proxy CORS. Pour montrer comment cela fonctionne, voici d'abord du code qui n'utilise pas de 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 raison pour laquelle le bloc catch est frappé ici est que le navigateur empêche ce code d'accéder à la réponse qui revient de https://example.com. Et la raison pour laquelle le navigateur fait cela est que la réponse n'a pas l'en-tête de réponse Access-Control-Allow-Origin. Maintenant, voici exactement le même exemple, mais avec un proxy CORS ajouté :

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?"))

***Remarque : Si https://cors-anywhere.herokuapp.com est en panne ou indisponible lorsque vous essayez, alors voyez ci-dessous comment déployer votre propre serveur CORS Anywhere sur Heroku en seulement 2 ou 3 minutes. Le deuxième extrait de code ci-dessus peut accéder à la réponse avec succès parce qu'en prenant l'URL de la requête et en la changeant en https://cors-anywhere.herokuapp.com/https://example.com-by, en la préfixant simplement avec l'URL du proxy, la requête est effectuée par le biais de ce proxy, ce qui a pour effet de.. :

  1. Transmet la demande à https://example.com.
  2. Reçoit la réponse de https://example.com.
  3. Ajoute l'en-tête Access-Control-Allow-Origin à la réponse.
  4. Transmet cette réponse, avec cet en-tête ajouté, au code frontal demandeur. Le navigateur autorise alors le code frontal à accéder à la réponse, car cette réponse avec l'en-tête Access-Control-Allow-Origin est ce que le navigateur voit. Vous pouvez facilement exécuter votre propre proxy en utilisant le code de https://github.com/Rob--W/cors-anywhere/.
    Vous pouvez également déployer facilement votre propre proxy sur Heroku en 2 ou 3 minutes seulement, avec 5 commandes :
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

Après avoir exécuté ces commandes, vous vous retrouverez avec votre propre serveur CORS Anywhere exécuté à, par exemple, https://cryptic-headland-94862.herokuapp.com/. Ainsi, au lieu de préfixer l'URL de votre requête avec https://cors-anywhere.herokuapp.com, préfixez-la avec l'URL de votre propre instance ; par exemple, https://cryptic-headland-94862.herokuapp.com/https://example.com. Si vous essayez d'utiliser https://cors-anywhere.herokuapp.com, vous constatez qu'il est hors service (ce qui arrive parfois), pensez à ouvrir un compte Heroku (si ce n'est pas déjà le cas) et prenez 2 ou 3 minutes pour suivre les étapes ci-dessus afin de déployer votre propre serveur CORS Anywhere sur Heroku. Que vous utilisiez votre propre serveur ou que vous utilisiez https://cors-anywhere.herokuapp.com ou un autre proxy ouvert, cette solution fonctionnera même si la requête déclenche chez les navigateurs une requête OPTIONS de preflight CORS - car dans ce cas, le proxy renvoie également les en-têtes Access-Control-Allow-Headers et Access-Control-Allow-Methods nécessaires à la réussite du preflight.

**Comment éviter le contrôle préalable CORS ? Le code dans la question déclenche un contrôle préalable CORS, car il envoie un en-tête Authorization. https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests Même sans cela, l'en-tête Content-Type : application/json déclencherait également le contrôle préalable. Ce que "preflight" signifie : avant que le navigateur n'essaie le POST dans le code de la question, il enverra d'abord une requête OPTIONS au serveur - pour déterminer si le serveur accepte de recevoir un POST d'origine croisée qui inclut les en-têtes Authorization et Content-Type : application/json. Cela fonctionne plutôt bien avec un petit script curl - j'obtiens mes données. Pour tester correctement avec curl, vous devez émuler la requête preflight OPTIONS que le navigateur envoie :

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"

...avec https://the.sign_in.url remplacé par votre URL sign_in réelle. La réponse que le navigateur doit voir à partir de cette requête OPTIONS doit inclure des en-têtes comme celui-ci :

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

Si la réponse OPTIONS n'inclut pas ces en-têtes, le navigateur s'arrêtera là et ne tentera même pas d'envoyer la requête POST. De plus, le code d'état HTTP de la réponse doit être un 2xx, généralement 200 ou 204. S'il s'agit d'un autre code d'état, le navigateur s'arrête tout de suite. Le serveur en question répond à la requête OPTIONS avec un code d'état 501, ce qui signifie apparemment qu'il essaie d'indiquer qu'il ne prend pas en charge les requêtes OPTIONS. Les autres serveurs répondent généralement par un code d'état 405 "Method not allowed" dans ce cas. Vous ne serez donc jamais en mesure d'effectuer des requêtes POST directement sur ce serveur à partir de votre code JavaScript frontal si le serveur répond à cette requête OPTIONS avec un 405 ou 501 ou tout autre code que 200 ou 204 ou s'il ne répond pas avec les en-têtes de réponse nécessaires. La façon d'éviter de déclencher un contrôle en amont dans le cas de la question serait la suivante :

  • si le serveur n'a pas besoin d'un en-tête de requête Authorization mais s'appuie (par exemple) sur les données d'authentification intégrées dans le corps de la requête POST ou comme paramètre de requête
  • si le serveur n'exigeait pas que le corps de la requête POST ait un type de média Content-Type : application/json mais acceptait à la place le corps de la requête POST comme application/x-www-form-urlencoded avec un paramètre nommé json (ou autre) dont la valeur est la donnée JSON.

    *Comment résoudre les problèmes de "Access-Control-Allow-Origin header must not be the wildcard "* ?

    Je reçois un autre message d'erreur :

    La valeur de l'en-tête 'Access-Control-Allow-Origin&#39 ; dans la réponse ne doit pas être le caractère générique '&#39 ; lorsque le mode d'authentification de la requête est 'include&#39 ;. L'origine 'http://127.0.0.1:3000&#39 ; n'est donc pas autorisée l'accès. Le mode d'identification des requêtes initiées par l'application XMLHttpRequest est contrôlé par l'attribut withCredentials. Pour une demande qui inclut des informations d'identification, les navigateurs ne laisseront pas votre code JavaScript frontal accéder à la réponse si la valeur de l'en-tête de réponse Access-Control-Allow-Origin est `. Dans ce cas, la valeur doit correspondre exactement à l'origine de votre code frontal,http://127.0.0.1:3000. Voir [*Demandes créditées et caractères génériques*][1] dans l'article de MDN sur le contrôle d'accès HTTP (CORS). Si vous contrôlez le serveur auquel vous envoyez la requête, une façon courante de traiter ce cas est de configurer le serveur pour qu'il prenne la valeur de l'en-tête de requêteOriginet la renvoie dans l'en-tête de réponseAccess-Control-Allow-Origin`. Par exemple, avec nginx :

add_header Access-Control-Allow-Origin $http_origin

Mais ce n'est qu'un exemple ; d'autres systèmes de serveurs (web) fournissent des moyens similaires pour faire écho aux valeurs d'origine.

J'utilise Chrome. J'ai également essayé d'utiliser le plugin CORS de Chrome. Le plugin CORS de Chrome injecte apparemment, de manière simple et intelligente, un en-tête Access-Control-Allow-Origin : * dans la réponse que le navigateur voit. Si le plugin était plus intelligent, il mettrait la valeur de ce faux en-tête de réponse Access-Control-Allow-Origin à l'origine réelle de votre code JavaScript frontal, http://127.0.0.1:3000. Évitez donc d'utiliser ce plugin, même pour des tests. Ce n'est qu'une distraction. Si vous voulez tester les réponses que vous obtenez du serveur sans que le navigateur ne les filtre, vous feriez mieux d'utiliser curl -H comme ci-dessus.

En ce qui concerne le code JavaScript frontal pour la requête fetch(...) dans la question :

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

Supprimez ces lignes. Les en-têtes Access-Control-Allow-* sont des en-têtes de réponse. Vous ne devez jamais les envoyer dans une requête. Le seul effet que cela aura sera de déclencher un navigateur pour faire un preflight. [1] : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Credentialed_requests_and_wildcards

Commentaires (8)

Cette erreur se produit lorsque l'URL du client et l'URL du serveur ne correspondent pas, y compris le numéro de port. Dans ce cas, vous devez activer votre service pour CORS, c'est-à-dire le partage des ressources entre les origines.

Si vous hébergez un service Spring REST, vous pouvez le trouver dans l'article de blog [CORS support in Spring Framework][1].

Si vous hébergez un service utilisant un serveur Node.js alors

  1. Arrêtez le serveur Node.js.

  2. npm install cors --save.

  3. Ajoutez les lignes suivantes à votre server.js

    var cors = require('cors&#39 ;)

    app.use(cors()) // Utilisez ceci après la déclaration de la variable

[1] : https://spring.io/blog/2015/06/08/cors-support-in-spring-framework

Commentaires (4)

Enlevez ça :

credentials: 'include',
Commentaires (0)