web-dev-qa-db-fra.com

AngularJS effectue une requête HTTP OPTIONS pour une ressource d'origine croisée.

J'essaie de configurer AngularJS pour qu'il communique avec une ressource d'origine croisée où l'hôte d'actif qui fournit mes fichiers de modèle se trouve sur un domaine différent. Par conséquent, la demande XHR que angular effectue doit être interdomaine. J'ai ajouté l'en-tête CORS approprié sur mon serveur pour que la requête HTTP fonctionne, mais cela ne semble pas fonctionner. Le problème est que lorsque j'examine les requêtes HTTP dans mon navigateur (chrome), la requête envoyée au fichier d'actif est une requête OPTIONS (il doit s'agir d'une requête GET).

Je ne sais pas s'il s'agit d'un bogue dans AngularJS ou si j'ai besoin de configurer quelque chose. D'après ce que je comprends, le wrapper XHR ne peut pas créer de requête HTTP OPTIONS. Il semble donc que le navigateur essaie de déterminer s'il est "autorisé" à télécharger l'actif avant d'effectuer la requête GET. Si tel est le cas, dois-je définir l'en-tête CORS (Access-Control-Allow-Origin: http://asset.Host .. .) Avec l'hôte de l'actif également?

253
matsko

Les requêtes OPTIONS ne sont en aucun cas un bogue AngularJS, c’est la façon dont le standard de partage de ressources Cross-Origin impose aux navigateurs de se comporter. Veuillez vous référer à ce document: https://developer.mozilla.org/en-US/docs/HTTP_access_control , où, dans la section "Présentation", il est indiqué:

La norme de partage des ressources d'origine croisée fonctionne en ajoutant un nouveau HTTP en-têtes qui permettent aux serveurs de décrire l'ensemble des origines qui sont autorisé à lire ces informations à l’aide d’un navigateur Web. Aditionellement, pour les méthodes de requête HTTP pouvant provoquer des effets secondaires sur les données utilisateur (en particulier .__; particulier; pour les méthodes HTTP autres que GET ou pour l'utilisation de POST avec certains types MIME). La spécification exige que les navigateurs "preflight" la requête, en sollicitant les méthodes prises en charge du serveur avec un en-tête de requête HTTP OPTIONS, puis après "approbation" de le serveur, en envoyant la demande réelle avec la demande HTTP réelle méthode. Les serveurs peuvent également indiquer aux clients si des "informations d'identification" (y compris les cookies et les données d’authentification HTTP) doivent être envoyés avec demandes.

Il est très difficile de fournir une solution générique qui conviendrait à tous les serveurs WWW, car la configuration varie en fonction du serveur lui-même et des verbes HTTP que vous souhaitez prendre en charge. Je vous encourage à lire cet excellent article ( http://www.html5rocks.com/fr/tutorials/cors/ ) qui contient beaucoup plus de détails sur les en-têtes exacts à envoyer par un serveur.

220

Pour Angular 1.2.0rc1 +, vous devez ajouter une ressourceUrlWhitelist.

1.2: version ils ont ajouté une fonction escapeForRegexp pour que vous n'ayez plus à échapper aux chaînes. Vous pouvez simplement ajouter l'URL directement 

'http://sub*.assets.example.com/**' 

assurez-vous d'ajouter ** pour les sous-dossiers. Voici un jsbin de travail pour 1.2: http://jsbin.com/olavok/145/edit


1.2.0rc: Si vous utilisez toujours une version rc, la solution angulaire 1.2.0rc1 ressemble à ceci:

.config(['$sceDelegateProvider', function($sceDelegateProvider) {
     $sceDelegateProvider.resourceUrlWhitelist(['self', /^https?:\/\/(cdn\.)?yourdomain.com/]);
 }])

Voici un exemple jsbin dans lequel cela fonctionne pour 1.2.0rc1: http://jsbin.com/olavok/144/edit


Pre 1.2: Pour les versions antérieures (ref http://better-inter.net/enabling-cors-in-angular-js/ ), vous devez ajouter les 2 lignes suivantes à votre configuration:

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];

Voici un exemple jsbin où cela fonctionne pour les versions antérieures à la version 1.2: http://jsbin.com/olavok/11/edit

67
JStark

NOTE: Je ne suis pas sûr que cela fonctionne avec la dernière version de Angular.

ORIGINAL:

Il est également possible de remplacer la demande OPTIONS (n'a été testée que dans Chrome):

app.config(['$httpProvider', function ($httpProvider) {
  //Reset headers to avoid OPTIONS request (aka preflight)
  $httpProvider.defaults.headers.common = {};
  $httpProvider.defaults.headers.post = {};
  $httpProvider.defaults.headers.put = {};
  $httpProvider.defaults.headers.patch = {};
}]);
61
user2899845

Votre service doit répondre à une requête OPTIONS avec des en-têtes tels que ceux-ci:

Access-Control-Allow-Origin: [the same Origin from the request]
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]

Voici un bon doc: http://www.html5rocks.com/fr/tutorials/cors/#toc-adding-cors-support-to-the-server

34
user2304582

Le même document dit 

Contrairement aux demandes simples (voir ci-dessus), les demandes "pré-contrôlées" envoient d'abord un en-tête de demande HTTP OPTIONS à la ressource de l'autre domaine, afin de déterminer si l'envoi de la demande est sans danger. Les requêtes intersites font l’objet d’un contrôle en amont de la sorte car elles peuvent avoir des conséquences sur les données de l’utilisateur. En particulier, une demande est contrôlée en amont si:

Il utilise des méthodes autres que GET ou POST. De même, si POST est utilisé pour envoyer des données de demande avec un type de contenu autre que application/x-www-form-urlencoded, multipart/form-data ou text/plain, par exemple. si la demande POST envoie une charge XML au serveur à l'aide de application/xml ou text/xml, la demande est contrôlée en amont.

Il définit des en-têtes personnalisés dans la demande (par exemple, la demande utilise un en-tête tel que X-PINGOTHER).

Lorsque la requête d'origine est Get sans en-têtes personnalisés, le navigateur ne doit pas émettre de requête d'options comme il le fait maintenant. Le problème est qu'il génère un en-tête X-Requested-With qui force la requête Options. Voir https://github.com/angular/angular.js/pull/1454 pour savoir comment supprimer cet en-tête.

20
Grum Ketema

Cela a résolu mon problème:

$http.defaults.headers.post["Content-Type"] = "text/plain";
10
Cem Arguvanlı

Si vous utilisez un serveur nodeJS, vous pouvez utiliser cette bibliothèque, elle a bien fonctionné pour moi https://github.com/expressjs/cors

var express = require('express')
  , cors = require('cors')
  , app = express();

app.use(cors());

et après vous pouvez faire un npm update.

10
Zakaria.dem

Voici comment j'ai corrigé ce problème sous ASP.NET

  • Tout d'abord, vous devez ajouter le package de nuget Microsoft.AspNet.WebApi.Cors 

  • Puis modifiez le fichier App_Start\WebApiConfig.cs 

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
    
  • Ajouter cet attribut sur votre classe de contrôleur

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {  
        [AcceptVerbs("POST")]
        public IHttpActionResult Post([FromBody]YourDataType data)
        {
             ...
             return Ok(result);
        }
    }
    
  • J'ai pu envoyer JSON à l'action de cette façon

    $http({
            method: 'POST',
            data: JSON.stringify(data),
            url: 'actionurl',
            headers: {
                'Content-Type': 'application/json; charset=UTF-8'
            }
        }).then(...)
    

Référence:Activation des requêtes croisées dans l'API Web ASP.NET 2

4
asfez

D'une certaine manière je l'ai corrigé en changeant 

<add name="Access-Control-Allow-Headers" 
     value="Origin, X-Requested-With, Content-Type, Accept, Authorization" 
     />

à 

<add name="Access-Control-Allow-Headers" 
     value="Origin, Content-Type, Accept, Authorization" 
     />
2
Ved Prakash

Si vous utilisez Jersey pour les API REST, procédez comme suit. 

Vous n'êtes pas obligé de modifier la mise en œuvre de vos services Web.

Je vais expliquer pour Jersey 2.x 

1) Ajoutez d’abord un ResponseFilter comme indiqué ci-dessous 

import Java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;

public class CorsResponseFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext,   ContainerResponseContext responseContext)
    throws IOException {
        responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
        responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");

  }
}

2) puis dans le fichier web.xml, dans la déclaration de servlet du maillot, ajoutez ce qui suit 

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
    </init-param>
0
nondescript

Un peu tard pour la fête,

Si vous utilisez Angular 7 (ou 5/6/7) et PHP en tant qu'API et obtenez toujours cette erreur, essayez d'ajouter les options d'en-tête suivantes au point final (API PHP).

 header("Access-Control-Allow-Origin: *");
 header("Access-Control-Allow-Methods: PUT, GET, POST, PUT, OPTIONS, DELETE, PATCH");
 header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");

Note : Ce qu'il faut seulement, c'est Access-Control-Allow-Methods. Mais, je colle ici deux autres Access-Control-Allow-Origin et Access-Control-Allow-Headers, simplement parce que vous aurez besoin que tous ces éléments soient correctement définis pour que Angular App puisse communiquer correctement avec votre API.

J'espère que ça aide quelqu'un.

À votre santé.

0
Anjana Silva

J'ai arrêté d'essayer de résoudre ce problème.

Mon IIS web.config contenait le "Access-Control-Allow-Methods" approprié. J'ai essayé d'ajouter des paramètres de configuration à mon code angulaire, mais après avoir brûlé quelques heures en essayant d'obtenir que Chrome appelle un service Web JSON interdomaine, misérablement.

En fin de compte, j'ai ajouté une page Web idiote de gestionnaire ASP.Net, obtenu that pour appeler mon service Web JSON et renvoyer les résultats. Il était opérationnel en 2 minutes.

Voici le code que j'ai utilisé:

public class LoadJSONData : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string URL = "......";

        using (var client = new HttpClient())
        {
            // New code:
            client.BaseAddress = new Uri(URL);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("Authorization", "Basic AUTHORIZATION_STRING");

            HttpResponseMessage response = client.GetAsync(URL).Result;
            if (response.IsSuccessStatusCode)
            {
                var content = response.Content.ReadAsStringAsync().Result;
                context.Response.Write("Success: " + content);
            }
            else
            {
                context.Response.Write(response.StatusCode + " : Message - " + response.ReasonPhrase);
            }
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Et dans mon contrôleur angulaire ...

$http.get("/Handlers/LoadJSONData.ashx")
   .success(function (data) {
      ....
   });

Je suis sûr qu'il existe un moyen plus simple/générique de le faire, mais la vie est trop courte ... 

Cela a fonctionné pour moi et je peux maintenant continuer à travailler normalement !!

0
Mike Gledhill

Parfaitement décrit dans le commentaire de pkozlowski… .. J'avais une solution de travail avec AngularJS 1.2.6 et ASP.NET Web Api, mais lorsque j'ai mis à niveau AngularJS en 1.3.3, les demandes ont échoué.

  • La solution pour le serveur Web Api consistait à ajouter le traitement des requêtes OPTIONS au début de la méthode de configuration (plus d'infos dans cet article de blog ):

    app.Use(async (context, next) =>
    {
        IOwinRequest req = context.Request;
        IOwinResponse res = context.Response;
        if (req.Path.StartsWithSegments(new PathString("/Token")))
        {
            var Origin = req.Headers.Get("Origin");
            if (!string.IsNullOrEmpty(Origin))
            {
                res.Headers.Set("Access-Control-Allow-Origin", Origin);
            }
            if (req.Method == "OPTIONS")
            {
                res.StatusCode = 200;
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", "GET", "POST");
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", "authorization", "content-type");
                return;
            }
        }
        await next();
    });
    
0
Jiří Kavulák

Pour un projet IIS MVC 5/Angular CLI (Oui, je sais que votre problème concerne Angular JS) avec l'API, j'ai procédé comme suit:

web.config under <system.webServer> node

    <staticContent>
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>
    <httpProtocol>
      <customHeaders>
        <clear />
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, atv2" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
      </customHeaders>
    </httpProtocol>

global.asax.cs

protected void Application_BeginRequest() {
  if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) && Request.HttpMethod == "OPTIONS") {
    Response.Flush();
    Response.End();
  }
}

Cela devrait résoudre vos problèmes à la fois pour MVC et WebAPI sans avoir à effectuer toutes les autres tâches. J'ai ensuite créé un HttpInterceptor dans le projet CLI angulaire qui a été ajouté automatiquement dans les informations d'en-tête correspondantes. J'espère que cela aide quelqu'un dans une situation similaire.

0
Damon Drake