web-dev-qa-db-fra.com

Demande HTTP de Angular envoyé comme OPTIONS au lieu de POST

J'essaie d'envoyer des demandes HTTP de mon application angular.js à mon serveur, mais je dois résoudre certaines erreurs CORS.

La requête HTTP est effectuée à l'aide du code suivant:

functions.test = function(foo, bar) {
    return $http({
        method: 'POST',
        url: api_endpoint + 'test',
        headers: {
            'foo': 'value',
            'content-type': 'application/json'
        },
        data: {
            bar:'value'
        }
    });
};

Le premier essai s'est soldé par des erreurs CORS. J'ai donc ajouté les lignes suivantes à mon script PHP:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding, X-Auth-Token, content-type');

La première erreur est maintenant éliminée.

Maintenant, la console développeur de Chrome me montre les erreurs suivantes:

angular.js: 12011 OPTIONS http: // localhost: 8000/test (fonction anonyme)

423ef03a: 1 XMLHttpRequest ne peut pas charger http: // localhost: 8000/test . La réponse pour le contrôle en amont a un code d'état HTTP non valide 400

et la requête réseau ressemble à ce que j'attendais (état HTTP 400 est également attendu):

network request

Je ne peux pas imaginer comment résoudre la chose (et comment comprendre) pourquoi la demande sera envoyée sur localhost en tant que OPTIONS et aux serveurs distants en tant que POST. Existe-t-il une solution pour résoudre ce problème étrange?

16
David

La demande OPTIONS est appelée demande de pré-vol , qui fait partie de = Partage de ressources entre origines (CORS) . Les navigateurs l'utilisent pour vérifier si une demande est autorisée à partir d'un domaine particulier comme suit:

  1. Le navigateur veut envoyer une demande, disons une demande POST avec le application/json type de contenu
  2. Donc tout d'abord, il envoie la requête pré-volOPTIONS
  3. Si le serveur répond avec une réponse d'état non 2XX, le navigateur n'enverra pas la demande réelle (car il sait maintenant qu'elle serait refusée de toute façon)
  4. Si le navigateur a un HTTP 200 OK réponse à la demande de pré-vol, il envoie la demande réelle, POST dans votre cas

Théorie

Demandes simples

Les navigateurs n'envoient pas les requêtes avant le vol dans certains cas, ce sont les soi-disant simples demandes et sont utilisés dans les conditions suivantes:

  • L'une des méthodes autorisées:
    • GET
    • HEAD
    • POST
  • Hormis les en-têtes définis automatiquement par l'agent utilisateur (par exemple, Connection, User-Agent, etc.), les seuls en-têtes qui peuvent être définis manuellement sont les suivants:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (mais notez les exigences supplémentaires ci-dessous)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Les seules valeurs autorisées pour l'en-tête Content-Type sont:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • Aucun écouteur d'événement n'est enregistré sur un objet XMLHttpRequestUpload utilisé dans la demande; ceux-ci sont accessibles en utilisant le XMLHttpRequest.upload propriété.
  • Aucun objet ReadableStream n'est utilisé dans la demande.

Ces demandes sont envoyées directement et le serveur traite simplement avec succès la demande ou les réponses avec une erreur au cas où elle ne correspondrait pas aux règles CORS. Dans tous les cas, la réponse contiendra les en-têtes CORS Access-Control-Allow-*.

Demandes pré-volées

Les navigateurs envoient les demandes avant le vol si la demande réelle ne correspond pas à la demande simple conditions, le plus souvent:

  • types de contenu personnalisés tels que application/xml ou application/json, etc., sont utilisés
  • la méthode de demande est différente de GET, HEAD ou POST
  • la méthode POST est d'un autre type de contenu que application/x-www-form-urlencoded, multipart/form-data ou text/plain

Vous devez vous assurer que la réponse à la demande pré-vol possède les attributs suivants :

  • code d'état HTTP réussi, c'est-à-dire 200 OK
  • entête Access-Control-Allow-Origin: * (un caractère générique * permet une demande depuis n'importe quel domaine, vous pouvez utiliser n'importe quel domaine spécifique pour restreindre l'accès ici bien sûr)

De l'autre côté, le serveur peut refuser la requête CORS simplement en envoyant une réponse à la requête - pré-vol avec les attributs suivants:

  • code HTTP non réussi (c'est-à-dire autre que 2XX)
  • succès du code HTTP (par exemple 200 OK), mais sans en-tête CORS (c'est-à-dire Access-Control-Allow-*)

Voir documentation sur Mozilla Developer Network ou par exemple tutoriel CORS de HTML5Rocks pour plus de détails.


Réponse TL; DR

Donc, dans votre cas, l'en-tête approprié est présent, il vous suffit de vous assurer que le code d'état HTTP de la réponse pré-vol est 200 OK ou un autre succès (2XX) .

J'ai rencontré un problème très similaire lors de l'écriture d'une application Angular 2 - qui utilise un serveur NODE pour l'API.

Étant donné que je développe sur ma machine locale, je continuais à rencontrer des problèmes d'en-tête Cross Origin, lorsque j'essayais de POST vers l'API depuis mon Angular app.

La définition des en-têtes (dans le serveur de noeud) comme ci-dessous a fonctionné pour les demandes GET, mais mes demandes PUT ont continué à publier des objets vides dans la base de données.

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, 
Origin, Authorization, Accept, Client-Security-Token, Accept-
Encoding, X-Auth-Token, content-type');

Après avoir lu article de Dawid Ferenczy , j'ai réalisé que la requête PREFLIGHT envoyait des données vides à mon serveur, et c'est pourquoi mes entrées de base de données étaient vides, alors j'ai ajouté cette ligne dans le serveur NODE JS:

  if (req.method == "OPTIONS")
    {
        res.writeHead(200, {"Content-Type": "application/json"});
        res.end();
    }

Alors maintenant, mon serveur ignore la demande PREFLIGHT, (et retourne le statut 200, pour que le navigateur sache que tout est groovy ...) et de cette façon, la vraie demande peut passer et je reçois de vraies données publiées sur ma base de données!

2
b0rgBart3

Mettez juste

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("HTTP/1.1 200 ");
exit;}

au début de votre application côté serveur et ça devrait aller.

2
tmaringgele

Pour une application Spring Boot, pour activer la demande de cors, utilisez @CrossOrigin(origins = "*", maxAge = 3600) sur votre contrôleur respectif.

Référez-vous à ce document

1
Chintan Pandya