AngularJS esegue una richiesta HTTP OPTIONS per una risorsa di origine incrociata


264

Sto cercando di configurare AngularJS per comunicare con una risorsa di origine incrociata in cui l'host di risorse che fornisce i miei file modello si trova su un dominio diverso e quindi la richiesta XHR che Angular esegue deve essere tra domini. Ho aggiunto l'intestazione CORS appropriata al mio server affinché la richiesta HTTP funzioni, ma non sembra funzionare. Il problema è che quando controllo le richieste HTTP nel mio browser (chrome) la richiesta inviata al file delle risorse è una richiesta OPTIONS (dovrebbe essere una richiesta GET).

Non sono sicuro che si tratti di un bug in AngularJS o se devo configurare qualcosa. Da quanto ho capito, il wrapper XHR non può effettuare una richiesta HTTP OPTIONS, quindi sembra che il browser stia cercando di capire se è "autorizzato" a scaricare l'asset prima di eseguire la richiesta GET. In questo caso, allora devo impostare l'intestazione CORS (Access-Control-Allow-Origin: http://asset.host ... ) Anche con l'host delle risorse?

Risposte:


226

Le richieste OPTIONS non sono affatto un bug di AngularJS, ecco come lo standard di condivisione delle risorse tra le origini impone ai browser di comportarsi. Fare riferimento a questo documento: https://developer.mozilla.org/en-US/docs/HTTP_access_control , dove nella sezione "Panoramica" è indicato:

Lo standard di condivisione delle risorse tra origini funziona aggiungendo nuove intestazioni HTTP che consentono ai server di descrivere l'insieme di origini a cui è consentito leggere tali informazioni utilizzando un browser Web. Inoltre, per i metodi di richiesta HTTP che possono causare effetti collaterali sui dati dell'utente (in particolare; per metodi HTTP diversi da GET o per l'utilizzo POST con determinati tipi MIME). La specifica impone che i browser "eseguano il preflight" della richiesta, sollecitando i metodi supportati dal server con un'intestazione di richiesta OPTIONS HTTP e quindi, dopo "approvazione" dal server, inviando la richiesta effettiva con il metodo di richiesta HTTP effettivo. I server possono anche notificare ai clienti se "credenziali" (inclusi cookie e dati di autenticazione HTTP) debbano essere inviati con richieste.

È molto difficile fornire una soluzione generica che funzioni per tutti i server WWW poiché l'installazione varierà a seconda del server stesso e dei verbi HTTP che si intende supportare. Vorrei incoraggiarvi a superare questo eccellente articolo ( http://www.html5rocks.com/en/tutorials/cors/ ) che contiene molti più dettagli sulle intestazioni esatte che devono essere inviate da un server.


23
@matsko Puoi approfondire cosa hai fatto per risolvere questo problema? Sto affrontando lo stesso problema per cui una richiesta $resource POST di AngularJS sta generando una richiesta OPTIONS al mio server ExpressJS back-end (sullo stesso host; ma una porta diversa).
dbau,

6
Per tutti gli elettori negativi - è quasi impossibile fornire un'impostazione di configurazione esatta per tutti i server Web là fuori - la risposta richiederebbe 10 pagine in questo caso. Invece ho collegato ad un articolo che fornisce maggiori dettagli.
pkozlowski.opensource,

5
Hai ragione a non poter prescrivere una risposta: dovrai aggiungere intestazioni alla tua risposta OPTIONS che copra tutte le intestazioni richieste dal browser, nel mio caso, usando Chrome sono state le intestazioni 'accetta' e 'x -si invita-con'. In Chrome, ho capito cosa dovevo aggiungere guardando la richiesta di rete e vedendo che cosa Chrome stava chiedendo. Sto usando nodejs / expressjs come supporto, quindi ho creato un servizio che ha restituito una risposta alla richiesta OPTIONS che copre tutte le intestazioni richieste. -1 perché non ho potuto usare questa risposta per capire cosa fare, ho dovuto capirlo da solo.
Ed Sykes,

2
So che sono passati più di 2 anni, ma ... L'OP fa riferimento a richieste GET più volte (enfasi aggiunta da me): «[...] la richiesta inviata al file delle risorse è una richiesta OPTIONS ( dovrebbe essere un GET richiesta ). » e «il browser sta cercando di capire se è" autorizzato "a scaricare l'asset prima di eseguire la richiesta GET ». Come può non essere un bug di AngularJS allora? Le richieste di verifica preliminare non dovrebbero essere vietate per GET?
polettix,

4
@polettix anche la richiesta GET può attivare la richiesta pre-volo in un browser se vengono utilizzate intestazioni personalizzate. Controlla "richieste non così semplici" nell'articolo che ho collegato: html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request . Ancora una volta, è il meccanismo del browser , non AngularJS.
pkozlowski.opensource,

70

Per Angular 1.2.0rc1 + è necessario aggiungere una risorsaUrlWhitelist.

1.2: versione di rilascio hanno aggiunto una funzione escapeForRegexp in modo da non dover più sfuggire alle stringhe. Puoi semplicemente aggiungere l'URL direttamente

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

assicurati di aggiungere ** per le sottocartelle. Ecco un jsbin funzionante per 1.2: http://jsbin.com/olavok/145/edit


1.2.0rc: se si è ancora in una versione rc, Angular 1.2.0rc1 la soluzione è simile a:

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

Ecco un esempio di jsbin in cui funziona per 1.2.0rc1: http://jsbin.com/olavok/144/edit


Pre 1.2: per le versioni precedenti (ref http://better-inter.net/enabling-cors-in-angular-js/ ) è necessario aggiungere le seguenti 2 righe alla configurazione:

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

Ecco un esempio di jsbin in cui funziona per le versioni pre 1.2: http://jsbin.com/olavok/11/edit


Questo ha funzionato per me. Ecco la magia appropriata per il lato server con nodejs / express: gist.github.com/dirkk0/5967221
dirkk0

14
Questo funziona solo per la richiesta GET, ma non riesco ancora a trovare la soluzione per la richiesta POST su domini diversi.
Pnct

2
La combinazione di questa risposta con la risposta user2304582 sopra dovrebbe funzionare per le richieste POST. Devi dire al tuo server di accettare le richieste POST dal server esterno che lo ha inviato
Charlie Martin,

Questo non sembra che funzionerà da solo per me ... quindi un -1. Hai bisogno di qualcosa sullo stesso URL che risponderà a una richiesta OPTIONS come parte di un controllo pre-volo. Puoi spiegare perché questo dovrebbe funzionare?
Ed Sykes,

1
@EdSykes, ho aggiornato la risposta per 1.2 e ho aggiunto un esempio jsbin funzionante. Spero che dovrebbe risolverlo per te. Assicurati di premere il pulsante "Esegui con JS".
JStark,

62

NOTA: non sono sicuro che funzioni con l'ultima versione di Angular.

ORIGINALE:

È anche possibile ignorare la richiesta OPTIONS (è stata testata solo in 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 = {};
}]);

1
Funziona bene per me con Chrome, FF e IE 10. IE 9 e versioni precedenti devono essere testati nativamente su macchine Windows 7 o XP.
DOM

3
In alternativa si può impostare questo sul metodo di quella risorsa $ da solo e non a livello globale:$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
h7r

3
C'è qualche problema con questo? Sembra così semplice, eppure la risposta è così profonda nel thread? modifica: non ha funzionato affatto.
Sebastialonso,

Dove va questo codice? in app.js, controller.js o dove esattamente.
Murlidhar Fichadia,

Questo codice non fa nulla per una richiesta get. Ho aggiunto una regola separata per ottenere anche nella mia configurazione
kushalvm,

34

Il tuo servizio deve rispondere a una OPTIONSrichiesta con intestazioni come queste:

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]

Ecco un buon documento: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server


1
Per elaborare la parte [lo stesso ACCESS-CONTROL-REQUEST-HEADERS da richiesta], come ho già detto in un'altra risposta, è necessario esaminare la richiesta OPTIONS che il tuo browser sta aggiungendo. Quindi, aggiungi quelle intestazioni al servizio che stai costruendo (o web server). In expressjs che assomigliavano a: ejs.options (' ', funzione (richiesta, risposta) {response.header ('Access-Control-Allow-Origin', ' '); response.header ('Access-Control-Allow-Methods ',' GET, PUT, POST, DELETE '); response.header (' Access-Control-Allow-Headers ',' Content-Type, Authorization, accetta, x-richiesto-con '); response.send (); });
Ed Sykes,

1
Il commento di Ed Sykes è molto accurato, tieni presente che le intestazioni inviate sulla risposta OPTIONS e quelle inviate sulla risposta POST dovrebbero essere esattamente le stesse, ad esempio: Access-Control-Allow-Origin: * non è uguale a Access- Control-Allow-Origin: * a causa degli spazi.
Lucia,

20

Lo stesso documento dice

A differenza delle richieste semplici (discusse sopra), le richieste "preflight" inviano innanzitutto un'intestazione di richiesta OPTIONS HTTP alla risorsa sull'altro dominio, al fine di determinare se la richiesta effettiva è sicura da inviare. Le richieste tra siti sono preflight in questo modo poiché potrebbero avere implicazioni per i dati dell'utente. In particolare, viene preflight una richiesta se:

Utilizza metodi diversi da GET o POST. Inoltre, se POST viene utilizzato per inviare dati di richiesta con un tipo di contenuto diverso da application / x-www-form-urlencoded, multipart / form-data o text / plain, ad esempio se la richiesta POST invia un payload XML al server usando application / xml o text / xml, la richiesta viene preflight.

Imposta le intestazioni personalizzate nella richiesta (ad es. La richiesta utilizza un'intestazione come X-PINGOTHER)

Quando la richiesta originale è Ottieni senza intestazioni personalizzate, il browser non dovrebbe fare la richiesta Opzioni che fa ora. Il problema è che genera un'intestazione X-Requested-With che forza la richiesta Opzioni. Vedi https://github.com/angular/angular.js/pull/1454 su come rimuovere questa intestazione


10

Questo risolto il mio problema:

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

10

Se stai usando un server nodeJS, puoi usare questa libreria, ha funzionato bene per me https://github.com/expressjs/cors

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

app.use(cors());

e dopo puoi fare un npm update.


4

Ecco come ho risolto questo problema su ASP.NET

  • Innanzitutto, è necessario aggiungere il pacchetto nuget Microsoft.AspNet.WebApi.Cors

  • Quindi modificare il file App_Start \ WebApiConfig.cs

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
  • Aggiungi questo attributo alla tua classe controller

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {  
        [AcceptVerbs("POST")]
        public IHttpActionResult Post([FromBody]YourDataType data)
        {
             ...
             return Ok(result);
        }
    }
  • Sono stato in grado di inviare json all'azione in questo modo

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

Riferimento: abilitazione delle richieste di origine incrociata nell'API Web ASP.NET 2


2

In qualche modo l'ho risolto cambiando

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

per

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

0

Perfettamente descritto nel commento di Pkozlowski. Avevo una soluzione funzionante con AngularJS 1.2.6 e ASP.NET Web Api ma quando avevo aggiornato AngularJS alla 1.3.3, le richieste non erano riuscite.

  • La soluzione per il server Web Api consisteva nell'aggiungere la gestione delle richieste OPTIONS all'inizio del metodo di configurazione (maggiori informazioni in questo post del 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();
    });

Quanto sopra non funziona per me. Esiste una soluzione molto più semplice per abilitare CORS per l'uso con AngularJS con ASP.NET WEB API 2.2 e successive. Ottieni il pacchetto Microsoft WebAPI CORS da Nuget, quindi nel tuo file di configurazione WebAPI ... var cors = new EnableCorsAttribute ("www.example.com", " ", " "); config.EnableCors (cors); I dettagli sono sul sito Microsoft qui asp.net/web-api/overview/security/…
kmcnamee

0

Se stai usando Jersey per le API REST puoi fare come di seguito

Non è necessario modificare l'implementazione dei servizi Web.

Spiegherò per Jersey 2.x

1) Aggiungi prima un ResponseFilter come mostrato di seguito

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) quindi nel web.xml, nella dichiarazione servlet maglia aggiungere quanto segue

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

0

Ho rinunciato a cercare di risolvere questo problema.

Il mio web.config di IIS conteneva il " Access-Control-Allow-Methods" rilevante , ho sperimentato l'aggiunta di impostazioni di configurazione al mio codice angolare, ma dopo aver bruciato alcune ore nel tentativo di convincere Chrome a chiamare un servizio Web JSON tra domini, ho rinunciato miseramente.

Alla fine, ho aggiunto una stupida pagina Web del gestore ASP.Net, ho ottenuto che chiamasse il mio servizio Web JSON e restituisse i risultati. È stato installato e funzionante in 2 minuti.

Ecco il codice che ho usato:

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

E nel mio controller angolare ...

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

Sono sicuro che esiste un modo più semplice / generico di farlo, ma la vita è troppo breve ...

Questo ha funzionato per me e ora posso continuare a lavorare normalmente !!


0

Per un IIS MVC 5 / Angular CLI (Sì, sono ben consapevole che il tuo problema è con il progetto Angular JS) con API Ho fatto quanto segue:

web.config sotto <system.webServer>nodo

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

Ciò dovrebbe risolvere i tuoi problemi sia per MVC che per WebAPI senza dover fare tutto il resto. Ho quindi creato un HttpInterceptor nel progetto CLI angolare che ha aggiunto automaticamente le informazioni di intestazione pertinenti. Spero che questo aiuti qualcuno in una situazione simile.


0

Poco in ritardo alla festa,

Se stai utilizzando Angular 7 (o 5/6/7) e PHP come API e stai ancora riscontrando questo errore, prova ad aggiungere le seguenti opzioni di intestazione al punto finale (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");

Nota : ciò che richiede è solo Access-Control-Allow-Methods. Ma ne sto incollando altri due Access-Control-Allow-Origine Access-Control-Allow-Headers, semplicemente perché avrai bisogno di tutti questi per essere impostati correttamente per consentire all'App Angular di parlare correttamente con la tua API.

Spero che questo aiuti qualcuno.

Saluti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.