Condividi tramite


Il presente articolo è stato tradotto automaticamente.

ASP.NET Web API

Supporto CORS in ASP.NET Web API 2

Brock Allen

Cross-origine risorsa condivisione (CORS) è una specifica di World Wide Web Consortium (W3C) (comunemente considerata parte dell'HTML5) che permette di superare la restrizione di sicurezza stessa origine politica imposta dai browser JavaScript. La stessa origine politica significa che vostro JavaScript può solo effettuare chiamate AJAX indietro per la stessa origine della pagina Web contenente (dove "origine" è definito come la combinazione di nome, protocollo e porta il numero di host). Ad esempio, JavaScript in una pagina Web da http://foo.com non può effettuare chiamate AJAX http://bar.com (o http://www.foo.com, https://foo.com o http://foo.com:999, per quella materia).

CORS rilassa questa restrizione lasciando server indicano quali origini sono autorizzati a chiamare loro. CORS è applicati dai browser, ma deve essere implementato sul server, e la più recente release di ASP.NET Web API 2 ha CORS completo di supporto. Con Web API 2, è possibile configurare criteri per consentire ai accedere tua API JavaScript client da una diversa origine.

Basi CORS

Per utilizzare le nuove funzionalità CORS in Web API, è utile per capire i dettagli di CORS stessa, perché l'implementazione di Web API è true per la specifica. Questi dettagli potrebbero sembrare pedanti ora, ma sarà utile più tardi a capire le impostazioni disponibili in Web API — e quando stai debug CORS ti aiuterò risolvere i problemi più velocemente.

Meccanica generale del CORS è tali che quando JavaScript è tentare di fare una chiamata AJAX di croce-origine browser "chiederà" il server se questo è consentito inviando intestazioni di richiesta HTTP (ad esempio, origine). Il server indica ciò che è consentito restituendo le intestazioni HTTP nella risposta (ad esempio, accesso-controllo-consentire-origine). Questo permesso di controllare è fatto per ogni URL distinti il client richiama, ossia che URL diversi possono avere diverse autorizzazioni.

Oltre l'origine, CORS consente un server indicare quali metodi HTTP consentiti, quali intestazioni di un client può inviare, quali intestazioni di risposta HTTP, un client può leggere e se il browser può automaticamente inviare o ricevere le credenziali (biscotti o intestazioni di autorizzazione) di richiesta HTTP. Altre intestazioni di richiesta e risposta indicano che queste caratteristiche sono ammessi. Queste intestazioni sono ricapitolate Figura 1 (nota che alcune delle caratteristiche non hanno alcuna intestazione inviata nella richiesta — solo la risposta).

Figura 1 CORS intestazioni HTTP

Autorizzazione/caratteristica Intestazione della richiesta Intestazione di risposta
Origine Origine Accesso-controllo-consentire-origine
Metodo HTTP Controllo-richiesta-metodo di accesso Controllo-consentire-metodo di accesso
Intestazioni di richiesta Accesso-controllo-intestazioni di richiesta Accesso-controllo-consentire-header
Intestazioni di risposta   Accesso-controllo-esporre-header
Credenziali   Controllo-consentire-credenziali di accesso
Risposta di verifica preliminare della cache   Accesso-controllo-Max-Age

Browser può chiedere al server per queste autorizzazioni in due modi diversi: semplici richieste CORS e CORS verifica preliminare delle richieste.

Semplici richieste di CORS Ecco un esempio di una semplice richiesta CORS:

POST http://localhost/WebApiCorsServer/Resources/ HTTP/1.1
Host: localhost
Accept: */*
Origin: http://localhost:55912
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
value1=foo&value2=5

E la risposta:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912
Content-Length: 27
{"Value1":"foo","Value2":5}

La richiesta è una richiesta di croce-origine da http://localhost:55912 a http://localhost, e il browser aggiunge un'intestazione HTTP di origine nella richiesta di indicare l'origine chiamando al server. Il server risponde con un accesso -­intestazione di risposta origine controllo permettono che indica che questa origine è consentita. Il browser applica i criteri del server e il JavaScript riceverà il callback di successo normale.

Il server può rispondere con il valore esatto di origine dalla richiesta, oppure il valore "*" è consentita l'indicazione di qualsiasi origine. Se il server non avesse consentito l'origine chiamata, poi l'intestazione di accesso-controllo-consentire-origine sarebbe semplicemente assente e callback di errore di JavaScript chiamata vuoi essere richiamato.

Si noti che con un semplice CORS richiedere la chiamata sul server sono ancora richiamato. Questo può essere sorprendente se stai ancora imparando circa CORS, ma questo comportamento non è diverso da uno scenario dove il browser aveva costruito un < form > elemento e fatta una normale richiesta POST. CORS non impedisce la chiamata da essere richiamato sul server; piuttosto, impedisce il JavaScript chiamante riceve i risultati. Se si desidera evitare che il chiamante invocando il server, quindi occorre implementare una sorta di autorizzazione nel codice server (possibilmente con l'attributo di filtro [autorizza] autorizzazione).

L'esempio precedente è conosciuto come una semplice richiesta CORS, perché il tipo di chiamata AJAX dal client era un GET o un POST; il Content-Type è stata una delle applicazione/x-www-form-­urlencoded, multipart/form-data, o text/plain; e non non c'erano nessuna intestazione aggiuntiva richiesta inviata. Se la chiamata AJAX era un altro metodo HTTP, il Content-Type è stato un altro valore o il cliente ha voluto inviare intestazioni di richiesta aggiuntiva, quindi la richiesta sarebbe considerata una richiesta di verifica preliminare. La meccanica delle richieste di verifica preliminare è leggermente diversa.

CORS richieste di verifica preliminare se una chiamata AJAX non è una semplice richiesta, quindi richiede una verifica preliminare richiesta CORS, che è semplicemente un'ulteriore richiesta HTTP al server per ottenere l'autorizzazione. Questa richiesta di verifica preliminare viene fatto automaticamente dal browser e utilizza il metodo HTTP di opzioni. Se il server risponde correttamente alla richiesta di verifica preliminare e concede l'autorizzazione, il browser eseguirà la chiamata effettiva AJAX che JavaScript sta tentando di fare.

Se la prestazione è una preoccupazione (e quando non è vero?), poi l'esito di questa richiesta di verifica preliminare può essere memorizzato nella cache dal browser includendo l'accesso-controllo-Max-Age intestazione nella risposta verifica preliminare. Il valore contiene il numero di secondi per il quale le autorizzazioni possono essere memorizzati nella cache.

Ecco un esempio di una richiesta di verifica preliminare CORS:

OPTIONS http://localhost/WebApiCorsServer/Resources/1 HTTP/1.1
Host: localhost
Access-Control-Request-Method: PUT
Origin: http://localhost:55912
Access-Control-Request-Headers: content-type
Accept: */*

E la risposta di verifica preliminare:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:55912
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: content-typeAccess-Control-Max-Age: 600

Ecco la richiesta effettiva di AJAX:

PUT http://localhost/WebApiCorsServer/Resources/1 HTTP/1.1
Host: localhost
Content-Length: 27
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:55912
Content-Type: application/json
{"value1":"foo","value2":5}

E la risposta di AJAX:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912
Content-Length: 27
{"Value1":"foo","Value2":5}

Nota in questo esempio una richiesta di verifica preliminare CORS viene attivato perché è mettere il metodo HTTP e il cliente deve inviare l'intestazione Content-Type per indicare che la richiesta contiene applicazione/json. Nella richiesta di verifica preliminare (oltre a origine) il metodo richiesta di controllo accesso e accesso-controllo-intestazioni di richiesta richiesta intestazioni vengono utilizzate per chiedere il permesso per il tipo di metodo HTTP e l'intestazione aggiuntiva che il cliente desidera inviare.

Il server dell'autorizzazione (e imposta una verifica preliminare durata della cache) e quindi il browser ha permesso l'effettiva chiamata AJAX. Se il server non concede l'autorizzazione a una qualsiasi delle caratteristiche richieste, quindi l'intestazione di risposta corrispondente sarebbe stato assente, non sarebbe stata effettuata la chiamata AJAX e il callback di errore JavaScript sarebbe hai stato richiamato invece.

Le richieste HTTP e le risposte precedenti sono state realizzate utilizzando Firefox. Se utilizza Internet Explorer, si potrebbe notare un'intestazione Accept aggiuntiva richiesta. Se si dovesse utilizzare Chrome, vedreste accetta sia origine inoltre richiesto. Interessante, non vedrai che accetta o origine nell'accesso-controllo -­Consenti-intestazioni, come la specifica dice che stai impliciti e può essere omesso (che fa Web API). È un punto del dibattito se origine e Accept in realtà devono essere richiesti, ma dato come questi browser lavoro oggi, vostra politica Web API CORS molto probabilmente sarà necessario includerli. È un peccato che i fornitori di browser non sembrano essere coerenti nella loro lettura della specifica.

Intestazioni di risposta è facile dare un permesso client per accedere alle intestazioni di risposta utilizzando l'intestazione di risposta di accesso-controllo-esporre-Headers. Ecco un esempio di una risposta HTTP che consente il chiamata JavaScript per accedere l'intestazione di risposta personalizzata "bar":

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912Access-Control-Expose-Headers: bar
bar: a bar value
Content-Length: 27
{"Value1":"foo","Value2":5}

Il client JavaScript può semplicemente utilizzare la funzione di getResponseHeader XMLHttpRequest per leggere il valore. Ecco un esempio di utilizzo di jQuery:

$.ajax({
  url: "http://localhost/WebApiCorsServer/Resources/1",
  // other settings omitted
}).done(function (data, status, xhr) {
  var bar = xhr.getResponseHeader("bar");
  alert(bar);
});

Credenziali e autenticazione forse l'aspetto più confusionaria di CORS ha a che fare con le credenziali e l'autenticazione. Generalmente, l'autenticazione con le API Web può essere fatto con un biscotto o con un'intestazione Authorization (ci sono altri modi, ma questi due sono i più comuni). Nell'attività normale browser, se uno di questi è stato stabilito in precedenza, quindi il browser implicitamente passerà questi valori al server richieste successive. Con AJAX croce-origine, però, questo passaggio implicito dei valori deve essere esplicitamente richiesta in JavaScript (tramite il flag withCredentials su XMLHttpRequest) e deve essere esplicitamente consentito nella politica di CORS del server (tramite l'intestazione di risposta-controllo-consentire-credenziali di accesso).

Ecco un esempio di un client JavaScript impostando il flag withCredentials con jQuery:

$.ajax({
  url: "http://localhost/WebApiCorsServer/Resources/1",
  xhrFields: {
    withCredentials: true
  }
  // Other settings omitted
});

La bandiera di withCredentials fa due cose: Se il server rilascia un cookie, il browser può accettarla; Se il browser dispone di un cookie, è possibile inviare al server.

Ecco un esempio di risposta HTTP che consente credenziali:

HTTP/1.1 200 OK
Set-Cookie: foo=1379020091825
Access-Control-Allow-Origin: http://localhost:55912
Access-Control-Allow-Credentials: true

L'intestazione di risposta-controllo-consentire-credenziali di accesso fa due cose: Se la risposta ha un cookie, il browser può accettarla; e se il browser ha inviato un cookie su richiesta, il client JavaScript possono ricevere i risultati della chiamata. In altre parole, se il client imposta withCredentials, quindi il cliente vedrà solo un callback di successo in JavaScript se il server (nella risposta) consente credenziali. Se è stato impostato il withCredentials e il server non consente le credenziali, il cliente non otterrà accesso ai risultati e verrà richiamato il callback di errore del client.

Lo stesso insieme di regole e comportamenti si applicano se l'intestazione di autorizzazione è usato anziché i cookie (ad esempio, quando si utilizza l'autenticazione di base o integrata di Windows). Una nota interessante sull'utilizzo di credenziali e l'intestazione di autorizzazione: Il server non deve concedere esplicitamente l'intestazione di autorizzazione nell'intestazione della risposta di accesso controllo intestazioni--consentire CORS.

Notare che con l'intestazione di risposta CORS controllo-consentire-credenziali di accesso, se il server rilascia questa intestazione, quindi il valore jolly "*" non può essere utilizzato per l'accesso-controllo-consentire-origine. La specifica CORS richiede invece l'origine esplicito per essere utilizzato. Il framework Web API gestisce tutto questo per voi, ma cito qui perché si potrebbe notare questo comportamento durante il debug.

C'è una svolta interessante a questa discussione di credenziali e autenticazione. La descrizione fino a questo punto è stato per lo scenario dove il browser è implicitamente invio di credenziali. È possibile che un client JavaScript inviare in modo esplicito le credenziali (ancora una volta, in genere tramite l'intestazione di autorizzazione). Se questo è il caso, allora nessuna delle suddette regole o comportamenti relativi alle credenziali si applica.

Per questo scenario, il cliente avrebbe esplicitamente impostare l'intestazione di autorizzazione su richiesta e non sarebbe necessario impostare withCredentials su XMLHttpRequest. Questa intestazione innescherebbe una richiesta di verifica preliminare e il server avrebbe bisogno permettere l'intestazione di autorizzazione con l'intestazione di risposta di accesso controllo intestazioni--consentire CORS. Inoltre, il server non avrebbe bisogno di rilasciare l'accesso-controllo -­intestazione di risposta Consenti credenziali CORS.

Ecco che cosa quel codice client assomiglierebbe esplicitamente impostare l'intestazione di autorizzazione:

$.ajax({
  url: "http://localhost/WebApiCorsServer/Resources/1",
  headers: {
    "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi..."
  }
  // Other settings omitted
});

Ecco la richiesta di verifica preliminare:

OPTIONS http://localhost/WebApiCorsServer/Resources/1 HTTP/1.1
Host: localhost
Access-Control-Request-Method: GET
Origin: http://localhost:55912
Access-Control-Request-Headers: authorization
Accept: */*

Ecco la risposta di verifica preliminare:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: authorization

Impostando in modo esplicito un valore token nell'intestazione autorizzazione è un approccio più sicuro di autenticazione perché si evita la possibilità di attacchi forgery (CSRF) richiesta. Si può vedere questo approccio dei nuovi modelli di pagina singola applicazione (SPA) in Visual Studio 2013.

Ora che avete visto le basi del CORS a livello HTTP, vi mostrerò come utilizzare il nuovo framework CORS per emettere queste intestazioni da Web API.

Supporto CORS in Web API 2

Il supporto CORS in Web API è un quadro completo per consentire un'applicazione definire le autorizzazioni per le richieste di CORS. Il quadro ruota intorno al concetto di una politica che consente di specificare le caratteristiche CORS a essere ammessi per qualsiasi data richiesta nell'applicazione.

In primo luogo, al fine di ottenere il quadro CORS, è necessario fare riferimento le librerie CORS dall'applicazione Web API (essi non riferimento per impostazione predefinita da uno qualsiasi dei modelli Web API in Visual Studio 2013). Il framework Web API CORS è disponibile via NuGet come il pacchetto Microsoft.AspNet.WebApi.Cors. Se non stai usando NuGet, è anche disponibile come parte di Visual Studio 2013 e voi avrete bisogno di due assembly di riferimento: System.Web.Http.Cors.dll e System.Web.Cors.dll (sulla mia macchina, questi sono situati in C:\Program Files (x86) \Microsoft ASP.NET\ASP.NET Web Stack 5\Packages).

Successivamente, per esprimere la politica, API Web fornisce una classe attribute personalizzata denominata EnableCorsAttribute. Questa classe contiene proprietà delle origini consentiti, metodi HTTP, richiedere le intestazioni, le intestazioni di risposta e se le credenziali sono consentite (quale modello di tutti i dettagli della specifica CORS discusso in precedenza).

Infine, affinché il framework Web API CORS elaborare le richieste del CORS ed emettono le intestazioni di risposta CORS appropriate, si deve guardare a ogni richiesta nell'applicazione. Web API ha un punto di estensibilità per tale intercettazione tramite gestori di messaggi. Opportunamente, il framework Web API CORS implementa un gestore di messaggio denominato CorsMessageHandler. Per le richieste CORS, sarà consultare la politica espressa nell'attributo per il metodo richiamato ed emettono le intestazioni di risposta appropriate CORS.

EnableCorsAttribute EnableCorsAttribute la classe è come un'applicazione può esprimere la sua politica CORS. La classe EnableCorsAttribute dispone di un costruttore di overload che accetta parametri tre o quattro. I parametri (in ordine) sono:

  1. Elenco delle origini ha permesso
  2. Elenco delle intestazioni di richiesta permesso
  3. Elenco dei metodi HTTP consentiti
  4. Elenco delle intestazioni di risposta consentita (opzionale)

C'è anche una proprietà per consentire le credenziali (supporti­credenziali) e un altro per specificare il valore della durata della verifica preliminare cache (PreflightMaxAge).

Figura 2 Mostra un esempio di applicazione dell'attributo EnableCors ai singoli metodi su un controller. I valori utilizzati per le varie impostazioni di criteri CORS devono corrispondere il CORS richieste e le risposte che sono state indicate negli esempi precedenti.

Figura 2 applicando l'attributo EnableCors ai metodi di azione

public class ResourcesController : ApiController
{
  [EnableCors("http://localhost:55912", // Origin
              null,                     // Request headers
              "GET",                    // HTTP methods
              "bar",                    // Response headers
              SupportsCredentials=true  // Allow credentials
  )]
  public HttpResponseMessage Get(int id)
  {
    var resp = Request.CreateResponse(HttpStatusCode.NoContent);
    resp.Headers.Add("bar", "a bar value");
    return resp;
  }
  [EnableCors("http://localhost:55912",       // Origin
              "Accept, Origin, Content-Type", // Request headers
              "PUT",                          // HTTP methods
              PreflightMaxAge=600             // Preflight cache duration
  )]
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  [EnableCors("http://localhost:55912",       // Origin
              "Accept, Origin, Content-Type", // Request headers
              "POST",                         // HTTP methods
              PreflightMaxAge=600             // Preflight cache duration
  )]
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
}

Avviso i parametri del costruttore è una stringa. Valori multipli sono indicati da un elenco delimitato da virgole (come specificato per le intestazioni di richiesta consentiti in Figura 2). Se si desidera consentire tutte le origini, le intestazioni di richiesta o metodi HTTP, è possibile utilizzare un "*" come valore (si deve ancora essere espliciti per le intestazioni di risposta).

Oltre ad applicare l'attributo di EnableCors a livello di metodo, è possibile anche applicarlo a livello di classe o globalmente all'applicazione. Il livello a cui è applicato l'attributo configura CORS per tutte le richieste a quel livello e di seguito nel codice Web API. Così, ad esempio, se applicate a livello di metodo, la politica sarà applicate esclusivamente alle richieste per quell'azione, considerando che se applicato a livello di classe, la politica sarà per tutte le richieste per tale controller. Infine, se applicato a livello globale, la politica sarà per tutte le richieste.

Di seguito è riportato un altro esempio di applicazione dell'attributo a livello di classe. Le impostazioni utilizzate in questo esempio sono abbastanza permissive, perché il carattere jolly è usato per origini consentiti, intestazioni di richiesta e metodi HTTP:

[EnableCors("*", "*", "*")]
public class ResourcesController : ApiController
{
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
}

Se c'è una politica a più posizioni, viene utilizzato l'attributo "più vicino" e gli altri vengono ignorati (quindi la precedenza è metodo, quindi classe, quindi globale). Se hai applicato la politica a un livello superiore, ma poi si desidera escludere una richiesta a un livello inferiore, è possibile utilizzare un'altra classe di attributo chiamata DisableCorsAttribute. Questo attributo, in sostanza, è una politica con senza autorizzazioni consentite.

Se avete altri metodi sul controller di cui non si desidera consentire CORS, è possibile utilizzare una delle due opzioni. In primo luogo, si può essere espliciti nell'elenco metodo HTTP, come mostrato Figura 3. O si può lasciare il jolly, ma escludere il metodo Delete con l'attributo DisableCors, come mostrato Figura 4.

Figura 3 utilizzando i valori espliciti per metodi HTTP

[EnableCors("*", "*", "PUT, POST")]
public class ResourcesController : ApiController
{
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  // CORS not allowed because DELETE is not in the method list above
  public HttpResponseMessage Delete(int id)
  {
    return Request.CreateResponse(HttpStatusCode.NoContent);
  }
}

Figura 4 utilizzando l'attributo DisableCors

[EnableCors("*", "*", "*")]
public class ResourcesController : ApiController
{
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  // CORS not allowed because of the [DisableCors] attribute
  [DisableCors]
  public HttpResponseMessage Delete(int id)
  {
    return Request.CreateResponse(HttpStatusCode.NoContent);
  }
}

CorsMessageHandler il CorsMessageHandler deve essere abilitato per il quadro CORS a svolgere il suo lavoro di intercettare le richieste per valutare la politica CORS ed emettono le intestazioni di risposta CORS. Abilitando il gestore di messaggi in genere avviene nella classe di configurazione dell'applicazione Web API richiamando il metodo di estensione EnableCors:

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other configuration omitted
    config.EnableCors();
  }
}

Se si desidera fornire una politica globale di CORS, è possibile passare un'istanza della classe EnableCorsAttribute come parametro al metodo EnableCors. Ad esempio, il seguente codice vuoi configurare un criterio di CORS permissivo globalmente all'interno dell'applicazione:

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other configuration omitted
    config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
  }
}

Come con qualsiasi gestore di messaggi, la CorsMessageHandler in alternativa possono essere registrati per ogni itinerario, piuttosto che a livello globale.

Quindi questo è tutto per il basic, "out of the box" quadro CORS in ASP.NET API Web 2. Una cosa bella di quadro è che è estensibile per scenari più dinamici, che guarderò avanti.

Personalizzazione politica

Dovrebbe essere evidente dagli esempi precedenti che l'elenco delle origini (se non è in uso il jolly) è un elenco statico compilato in codice Web API. Mentre questo potrebbe funzionare durante lo sviluppo o per scenari specifici, non è sufficiente se l'elenco delle origini (o altre autorizzazioni) deve essere determinato in modo dinamico (diciamo, da un database).

Fortunatamente, il quadro di CORS in Web API è estensibile tale che sostenere un elenco dinamico delle origini è facile. Infatti, il quadro è così flessibile che ci sono due approcci generali per personalizzare la generazione di politica.

Attributo personalizzato di politica CORS per consentire una dinamica politica CORS è possibile sviluppare una classe attribute personalizzata che può generare la politica da un'origine dati. Questa classe attribute personalizzata può essere utilizzata invece la classe di EnableCorsAttribute fornita da Web API. Questo approccio è semplice e conserva la grana fina si sentono di poter applicare un attributo su specifiche classi e metodi (e non si applica agli altri), come necessario.

Per implementare questo approccio, è semplicemente compilare un attributo personalizzato simile alla classe EnableCorsAttribute esistente. Il focus principale è l'interfaccia di ICorsPolicyProvider, che è responsabile della creazione di un'istanza di un CorsPolicy per qualsiasi data richiesta. Figura 5 contiene un esempio.

Figura 5 CORS personalizzata politica attributo

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
                AllowMultiple = false)]
public class EnableCorsForPaidCustomersAttribute :
  Attribute, ICorsPolicyProvider
{
  public async Task<CorsPolicy> GetCorsPolicyAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
  {
    var corsRequestContext = request.GetCorsRequestContext();
    var originRequested = corsRequestContext.Origin;
    if (await IsOriginFromAPaidCustomer(originRequested))
    {
      // Grant CORS request
      var policy = new CorsPolicy
      {
        AllowAnyHeader = true,
        AllowAnyMethod = true,
      };
      policy.Origins.Add(originRequested);
      return policy;
    }
    else
    {
      // Reject CORS request
      return null;
    }
  }
  private async Task<bool> IsOriginFromAPaidCustomer(
    string originRequested)
  {
    // Do database look up here to determine if origin should be allowed
    return true;
  }
}

La classe CorsPolicy ha tutte le proprietà di esprimere le autorizzazioni CORS da concedere. I valori utilizzati qui sono solo un esempio, ma presumibilmente si potrebbe essere popolati dinamicamente da una query di database (o da qualsiasi altra fonte).

Factory di Provider personalizzati politica il secondo approccio generale alla costruzione di una dinamica politica CORS è creare una factory di provider di criteri personalizzate. Questo è il pezzo del quadro CORS che ottiene il provider di criteri per la richiesta corrente. L'implementazione predefinita da API Web utilizza gli attributi personalizzati per individuare il provider di criteri (come si è visto in precedenza, la stessa classe di attributo era il provider di criteri). Questo è un altro pezzo pluggable del quadro CORS e si implementa la propria factory di provider di politica se si voleva utilizzare un approccio per una politica diversa da attributi personalizzati.

L'approccio basato sugli attributi descritto in precedenza fornisce un'associazione implicita da una richiesta da una politica. Un approccio di fabbrica del fornitore criteri personalizzati è diverso dall'approccio attributo perché richiede l'implementazione per fornire la logica per abbinare la richiesta in arrivo a un criterio. Questo approccio è più grossolana, in quanto è essenzialmente un approccio centralizzato per ottenere una politica CORS.

Figura 6 Mostra un esempio di ciò che potrebbe sembrare una factory di provider di criteri personalizzate. L'obiettivo principale in questo esempio è l'implementazione dell'interfaccia ICorsPolicyProviderFactory e relativo metodo GetCorsPolicyProvider.

Figura 6 Factory di Provider di criteri personalizzate

public class DynamicPolicyProviderFactory : ICorsPolicyProviderFactory
{
  public ICorsPolicyProvider GetCorsPolicyProvider(
    HttpRequestMessage request)
  {
    var route = request.GetRouteData();
    var controller = (string)route.Values["controller"];
    var corsRequestContext = request.GetCorsRequestContext();
    var originRequested = corsRequestContext.Origin;
    var policy = GetPolicyForControllerAndOrigin(
      controller, originRequested);
    return new CustomPolicyProvider(policy);
  }
  private CorsPolicy GetPolicyForControllerAndOrigin(
   string controller, string originRequested)
  {
    // Do database lookup to determine if the controller is allowed for
    // the origin and create CorsPolicy if it is (otherwise return null)
    var policy = new CorsPolicy();
    policy.Origins.Add(originRequested);
    policy.Methods.Add("GET");
    return policy;
  }
}
public class CustomPolicyProvider : ICorsPolicyProvider
{
  CorsPolicy policy;
  public CustomPolicyProvider(CorsPolicy policy)
  {
    this.policy = policy;
  }
  public Task<CorsPolicy> GetCorsPolicyAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
  {
    return Task.FromResult(this.policy);
  }
}

La differenza principale di questo approccio è che spetta interamente l'implementazione a determinare la politica dalla richiesta in arrivo. In Figura 6, il controller e l'origine potrebbe essere utilizzati per eseguire query su un database per i valori della politica. Ancora una volta, questo approccio è più flessibile, ma potenzialmente richiede più lavoro per determinare la politica dalla richiesta.

Per utilizzare il factory di provider di criteri personalizzate, è necessario registrarlo con Web API tramite il metodo di estensione SetCorsPolicyProviderFactory nella configurazione Web API:

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other configuration omitted
    config.EnableCors();
    config.SetCorsPolicyProviderFactory(
      new DynamicPolicyProviderFactory());
  }
}

Contributi di comunità in azione

ASP.NET Web API è un framework open source ed è parte di un più ampio insieme di open source framework collettivamente chiamati ASP.NET Web Stack, che comprende anche MVC, pagine Web e altri.

Questi quadri sono utilizzati per costruire la piattaforma ASP.NET e sono curati dal team di ASP.NET di Microsoft. Come curatore di una piattaforma open source, il team ASP.NET accoglie contributi comunitari e croce-origine risorsa condivisione implementazione (CORS) nel Web API è un tale contributo.

Esso è stato originariamente sviluppato da Brock Allen come parte della libreria di sicurezza IdentityModel thinktecture (thinktecture.github.io).

Debug CORS

Alcune tecniche vengono in mente debug CORS se (e quando) non funzionano le chiamate AJAX di croce-origine.

Lato client un approccio al debug è semplicemente utilizzare il debugger HTTP di scelta (ad esempio, Fiddler) e ispezionare tutte le richieste HTTP. Armati con le conoscenze raccolte in precedenza sui dettagli della specifica CORS, solitamente è possibile ordinare fuori perché una richiesta AJAX non è concesso autorizzazione controllando le intestazioni HTTP CORS (o la loro mancanza).

Un altro approccio è quello di utilizzare strumenti di sviluppo F12 del browser. La finestra di console browser moderni fornisce un messaggio di errore utile quando chiama un AJAX fallisce a causa di CORS.

Lato server framework il CORS stesso fornisce messaggi di dettagliate analisi utilizzando i servizi di analisi del Web API. Come un ITraceWriter è registrato con Web API, quadro CORS emetterà i messaggi con informazioni sul provider di criteri selezionati, i criteri utilizzati e le intestazioni HTTP CORS emesse. Per ulteriori informazioni su analisi Web API, consultare la documentazione dell'API Web su MSDN.

Una funzionalità molto richiesta

CORS è stata una caratteristica altamente richiesta ormai da tempo, ed infine è costruito a Web API. Questo articolo si concentra fortemente sui dettagli di CORS in sé, ma che la conoscenza è cruciale nell'implementazione e debug di CORS. Armati di questa conoscenza, dovrebbe essere in grado di utilizzare facilmente il supporto CORS in Web API per consentire a croce-origine chiama nelle vostre applicazioni.

Brock Allen è un consulente specializzato in Microsoft .NET Framework, sviluppo Web e sicurezza basata su Web. Egli è anche un istruttore per la società di formazione DevelopMentor, consulente associato per thinktecture GmbH & Co. KG, un collaboratore di thinktecture aprire progetti di origine e un collaboratore alla piattaforma ASP.NET . È possibile contattarlo sul suo sito Web, brockallen.com, o e-mail lui a brockallen@gmail.com.

Un ringraziamento al seguente esperto tecnico per la revisione dell'articolo:: Yao Huan Lin (Microsoft)
Yao Huang Lin (yaohuang@microsoft.com) è uno sviluppatore di software, il team di Web API ASP.NET di Microsoft. Ha lavorato su molti componenti di .NET Framework, tra cui ASP.NET, Windows Communication Foundation (WCF) e Windows Workflow Foundation (WF).