384 votes

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'essaie d'extraire des données de l'API REST de HP Alm. Cela fonctionne assez bien avec un petit curl script - j'obtiens mes données.

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

Fetch API ne peut pas charger . La réponse à la demande de contrôle préalable n'a pas passe pas le contrôle d'accès : Aucun en-tête 'Access-Control-Allow-Origin' n'est présent sur la ressource demandée. Origine ' http://127.0.0.1:3000 est n'est donc pas autorisé à y accéder. La réponse a le code d'état HTTP 501. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur no-cors' 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 de mise en œuvre ? 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'ai obtenu un autre message d'erreur :

La valeur de l'en-tête 'Access-Control-Allow-Origin' dans la réponse ne doit pas être le caractère générique '*' lorsque le mode d'identification de la demande est include'. Origine ' http://127.0.0.1:3000 n'est donc pas autorisé accès. Le mode d'authentification des requêtes initiées par la méthode XMLHttpRequest est contrôlé par l'attribut withCredentials.

667voto

sideshowbarker Points 29042

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

  • Comment utiliser un proxy CORS pour contourner le problème "Pas d'en-tête Access-Control-Allow-Origin" problèmes
  • Comment éviter le contrôle préalable CORS
  • Comment réparer "L'en-tête Access-Control-Allow-Origin ne doit pas être le joker" problèmes

Comment utiliser un proxy CORS pour contourner le problème "Pas d'en-tête Access-Control-Allow-Origin" problèmes

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 simplement l'absence de l'information nécessaire à l'exécution de la requête, alors vous ne pouvez pas contrôler le serveur. Access-Control-Allow-Origin vous pouvez quand même faire fonctionner les choses en faisant passer la requête par un proxy CORS. Pour montrer comment cela fonctionne, voici d'abord un 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 catch est atteint, le navigateur empêche ce code d'accéder à la réponse qui revient de l'application https://example.com . Et la raison pour laquelle le navigateur fait ça, c'est que la réponse n'a pas le caractère Access-Control-Allow-Origin en-tête de réponse.

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 hors service ou indisponible au moment où vous essayez, voyez ci-dessous comment déployer votre propre serveur CORS Anywhere sur Heroku en seulement 2-3 minutes.

Le deuxième extrait de code ci-dessus peut accéder à la réponse avec succès car il prend l'URL de la requête et la change en https://cors-anywhere.herokuapp.com/https://example.com -en la préfixant simplement avec l'URL du proxy - fait que la demande est faite par le biais de ce proxy, qui.. :

  1. Transférer la demande à https://example.com .
  2. Reçoit la réponse de https://example.com .
  3. Ajoute le Access-Control-Allow-Origin dans la réponse.
  4. Transmet cette réponse, avec cet en-tête ajouté, au code frontal demandeur.

Le navigateur permet alors au code frontal d'accéder à la réponse, car cette réponse avec l'attribut Access-Control-Allow-Origin L'en-tête de réponse est ce que le navigateur voit.

Vous pouvez facilement exécuter votre propre proxy en utilisant le code de l'application https://github.com/Rob--W/cors-anywhere/ .
Vous pouvez également déployer facilement votre propre proxy sur Heroku en 2 ou 3 minutes seulement, à l'aide de 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 fonctionnant à l'adresse suivante : par exemple, https://cryptic-headland-94862.herokuapp.com/ . Ainsi, plutôt que de préfixer l'URL de votre requête avec https://cors-anywhere.herokuapp.com préfixez-le avec l'URL de votre propre instance, par exemple, https://cryptic-headland-94862.herokuapp.com/https://example.com .

Donc si quand vous essayez d'utiliser https://cors-anywhere.herokuapp.com, vous trouvez que c'est en panne. (ce qui sera parfois le cas), 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.

Peu importe que vous gériez votre propre entreprise ou que vous utilisiez https://cors-anywhere.herokuapp.com ou un autre proxy ouvert, cette solution fonctionnera même si la requête est de celles qui déclenchent un contrôle préalable CORS de la part des navigateurs. OPTIONS car dans ce cas, le mandataire renvoie également la requête Access-Control-Allow-Headers y Access-Control-Allow-Methods les en-têtes nécessaires à la réussite du contrôle en amont.


Comment éviter le contrôle préalable CORS

Le code dans la question déclenche un contrôle préalable CORS, puisqu'il envoie un fichier Authorization en-tête.

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

Même sans cela, le Content-Type: application/json déclencherait également le contrôle préalable.

Ce que signifie "preflight" : avant que le navigateur n'essaye le fichier POST dans le code de la question, il enverra d'abord un fichier OPTIONS pour déterminer si le serveur accepte de recevoir une demande d'origine croisée. POST qui comprend le Authorization y Content-Type: application/json les en-têtes.

Cela fonctionne assez bien avec un petit curl script - j'obtiens mes données.

Pour tester correctement avec curl vous devez émuler le contrôle en amont. 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 ce que votre sign_in L'URL est.

La réponse que le navigateur doit voir de cette 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 le OPTIONS n'inclut pas ces en-têtes, le navigateur s'arrêtera là et ne tentera même pas d'envoyer l'adresse de l'utilisateur. POST demande. En outre, 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 là.

Le serveur dans la question répond au OPTIONS avec un code d'état 501, ce qui signifie apparemment qu'il essaie d'indiquer qu'il ne met pas en œuvre la prise en charge de la norme OPTIONS demandes. Les autres serveurs répondent généralement par un code d'état 405 "Méthode non autorisée" dans ce cas.

Donc, vous ne serez jamais en mesure de faire POST directement à ce serveur à partir de votre code JavaScript frontal si le serveur répond à cette demande. OPTIONS avec un 405 ou un 501 ou toute autre chose qu'un 200 ou un 204 ou si elle 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 préalable pour le cas de la question serait la suivante :

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

Comment réparer "L'en-tête Access-Control-Allow-Origin ne doit pas être le joker" problèmes

Je reçois un autre message d'erreur :

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

Pour une requête incluant des informations d'identification, les navigateurs ne permettront pas à votre code JavaScript frontal d'accéder à la réponse si la valeur de la balise Access-Control-Allow-Origin L'en-tête de réponse est * . Dans ce cas, la valeur doit correspondre exactement à l'origine de votre code frontal, http://127.0.0.1:3000 .

Ver Demandes accréditées et caractères génériques dans l'article 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'attribut Origin et le répercute dans la valeur de l'en-tête de la requête Access-Control-Allow-Origin l'en-tête de réponse. 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) offrent des moyens similaires de répercuter les valeurs d'origine.


J'utilise Chrome. J'ai également essayé d'utiliser le plugin CORS de Chrome.

Ce plugin CORS de Chrome injecte apparemment de manière simple et intelligente une Access-Control-Allow-Origin: * dans la réponse que le navigateur voit. Si le plugin était plus intelligent, il fixerait la valeur de ce faux en-tête 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, il est préférable d'utiliser le plugin curl -H comme ci-dessus.


En ce qui concerne le code JavaScript frontal de l'application fetch(…) dans la question :

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

Supprimez ces lignes. Le site Access-Control-Allow-* Les en-têtes sont réponse les en-têtes. 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 contrôle préalable.

80voto

Rakesh Points 1149

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 suivant Support CORS dans Spring Framework .

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')
    
    app.use(cors()) // Use this after the variable declaration

28voto

Lex Soft Points 170

Le problème est survenu parce que vous avez ajouté le code suivant en tant que demande dans votre front-end :

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

Ces en-têtes appartiennent à réponse et non une demande. Donc supprimer les, y compris la ligne :

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

Votre requête contenait 'Content-Type : application/json', ce qui a déclenché ce que l'on appelle le CORS preflight. Le navigateur a donc envoyé la requête avec la méthode OPTIONS. Voir Prévol CORS pour des informations détaillées.
Par conséquent, dans votre back-end vous devez traiter cette demande pré-vérifiée en renvoyant les en-têtes de réponse qui incluent :

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

Bien entendu, la syntaxe réelle dépend du langage de programmation que vous utilisez pour votre back-end.

Dans votre front-end, cela devrait être comme suit :

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

1voto

Nalan M Points 834

Enlevez ça :

credentials: 'include',

0voto

hoogw Points 1047

Utilisation de dataType: 'jsonp' a fonctionné pour moi.

   async function get_ajax_data(){
       var _reprojected_lat_lng = await $.ajax({
                                type: 'GET',
                                dataType: 'jsonp',
                                data: {},
                                url: _reprojection_url,
                                error: function (jqXHR, textStatus, errorThrown) {
                                    console.log(jqXHR)
                                },
                                success: function (data) {
                                    console.log(data);

                                    // note: data is already json type, you
                                    //       just specify dataType: jsonp
                                    return data;
                                }
                            });

 } // function

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X