Impostazione di Access-Control-Allow-Origin in ASP.Net MVC: il metodo più semplice possibile


206

Ho un semplice metodo d'azione, che restituisce un po 'di json. Funziona su ajax.example.com. Devo accedere a questo da un altro sito someothersite.com.

Se provo a chiamarlo, ottengo l'atteso ...:

Origin http://someothersite.com is not allowed by Access-Control-Allow-Origin.

Conosco due modi per aggirare questo: JSONP e la creazione di un HttpHandler personalizzato per impostare l'intestazione.

Non esiste un modo più semplice?

Non è possibile per una semplice azione definire un elenco di origini consentite o consentire semplicemente a tutti? Forse un filtro d'azione?

L'ottimale sarebbe ...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);

1
Dai un'occhiata qui per vNext e MVC6: neelbhatt40.wordpress.com/2015/09/10/…
Neel

Risposte:


382

Per semplici controller ASP.NET MVC

Crea un nuovo attributo

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
        base.OnActionExecuting(filterContext);
    }
}

Tagga la tua azione:

[AllowCrossSiteJson]
public ActionResult YourMethod()
{
    return Json("Works better?");
}

Per l'API Web ASP.NET

using System;
using System.Web.Http.Filters;

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext.Response != null)
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");

        base.OnActionExecuted(actionExecutedContext);
    }
}

Tagga un intero controller API:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

O singole chiamate API:

[AllowCrossSiteJson]
public IEnumerable<PartViewModel> Get()
{
    ...
}

Per Internet Explorer <= v9

IE <= 9 non supporta CORS. Ho scritto un javascript che instraderà automaticamente tali richieste attraverso un proxy. È tutto trasparente al 100% (devi solo includere il mio proxy e lo script).

Scaricalo usando nuget corsproxye segui le istruzioni incluse.

Post di blog | Codice sorgente


8
Sorprendente! Mi piace MVC + U!
Piotr Kula,

2
in soggezione dell'eleganza di questa soluzione
BraveNewMath

3
È possibile estendere facilmente l'attributo per accettare un'origine specifica se si desidera limitare CORS ai propri domini.
Petrus Theron,

2
Dovresti essere in grado di aggiungere questo a RegisterHttpFilters nel tuo App_Start \ FilterConfig corretto? In questo modo verrebbe applicato a tutti i controller Api nel progetto. Associandolo al commento di Pate sopra, potresti limitare CORS ai tuoi domini per tutti i controller.
bdwakefield,

9
Di recente ho aggiornato il nostro progetto a MVC 5 e ho provato a farlo. Anche l'aggiunta dell'intestazione in un filtro non sembra funzionare. Quando visualizzo la richiesta in rete, l'intestazione non è presente sulla risposta. C'è qualcos'altro che deve essere fatto per farlo funzionare?
Kneemin,

121

Se si utilizza IIS 7+, è possibile inserire un file web.config nella radice della cartella con questo nella sezione system.webServer:

<httpProtocol>
   <customHeaders>
      <clear />
      <add name="Access-Control-Allow-Origin" value="*" />
   </customHeaders>
</httpProtocol>

Vedi: http://msdn.microsoft.com/en-us/library/ms178685.aspx E: http://enable-cors.org/#how-iis7


1
Non ricordo più perché, ma questo metodo non funziona sempre in IIS 7+
LaundroMatt

Hmm. L'unica ragione per cui posso pensare che non funzionerebbe è se una richiesta proviene da un browser non CORS. Ma continuerò a indagare.
Sellmeadog

29
Inoltre, ciò renderebbe l'intero sito Web compatibile con CORS. Se qualcuno vuole contrassegnare una sola azione o controller come CORS-friendly, la risposta accettata è molto meglio.
Lev Dubinets,

1
Se vedi la sezione ASP.Net , ha un suggerimento : "Nota: questo approccio è compatibile con IIS6, IIS7 Classic Mode e IIS7 Integrated Mode."
percebus,

1
Sto riscontrando un problema tra domini quando pubblico la mia app in ambiente SharePoint. Quando eseguo la mia app in ambiente locale la mia app funziona correttamente, ma quando la pubblico su azzurro sul mio sito sharepoint reindirizza alla pagina di errore su Ajax. Inizia modulo chiamata. Ho provato questa soluzione ma non funziona per me. C'è qualche altra alternativa ad esso?
Jyotsna Wadhwani,

22

Ho riscontrato un problema in cui il browser ha rifiutato di pubblicare contenuti che aveva recuperato quando la richiesta passava nei cookie (ad esempio, lo xhr aveva i suoi withCredentials=true) e il sito era Access-Control-Allow-Originimpostato su *. (L'errore in Chrome era: "Impossibile utilizzare il carattere jolly in Access-Control-Allow-Origin quando il flag delle credenziali è true.")

Sulla base della risposta di @jgauffin, ho creato questo, che è fondamentalmente un modo per aggirare quel particolare controllo di sicurezza del browser, quindi avvertimento emptor.

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // We'd normally just use "*" for the allow-origin header, 
        // but Chrome (and perhaps others) won't allow you to use authentication if
        // the header is set to "*".
        // TODO: Check elsewhere to see if the origin is actually on the list of trusted domains.
        var ctx = filterContext.RequestContext.HttpContext;
        var origin = ctx.Request.Headers["Origin"];
        var allowOrigin = !string.IsNullOrWhiteSpace(origin) ? origin : "*";
        ctx.Response.AddHeader("Access-Control-Allow-Origin", allowOrigin);
        ctx.Response.AddHeader("Access-Control-Allow-Headers", "*");
        ctx.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        base.OnActionExecuting(filterContext);
    }
}

Questo è stato particolarmente utile, grazie.
cklimowski il

15

Questo è davvero semplice, basta aggiungerlo in web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="http://localhost" />
      <add name="Access-Control-Allow-Headers" value="X-AspNet-Version,X-Powered-By,Date,Server,Accept,Accept-Encoding,Accept-Language,Cache-Control,Connection,Content-Length,Content-Type,Host,Origin,Pragma,Referer,User-Agent" />
      <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, OPTIONS" />
      <add name="Access-Control-Max-Age" value="1000" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

In Origin metti tutti i domini che hanno accesso al tuo server web, nelle intestazioni metti tutte le possibili intestazioni che qualsiasi richiesta http ajax può usare, nei metodi metti tutti i metodi che permetti sul tuo server

Saluti :)


L'aggiunta di "Autorizzazione" in Access-Control-Allow-Header può anche essere utile se si intende utilizzare query autorizzate.
Dopo il

9

A volte anche il verbo OPTIONS causa problemi

Semplicemente: aggiorna il tuo web.config con il seguente

<system.webServer>
    <httpProtocol>
        <customHeaders>
          <add name="Access-Control-Allow-Origin" value="*" />
          <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

E aggiorna le intestazioni webservice / controller con httpGet e httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {

A proposito, in sitefinity è necessario aggiungere * alle impostazioni avanzate di sistema nella sezione sicurezza
Bishoy Hanna,

quali sono i file in cui devo aggiornare le intestazioni del controller?
user3281466


5

Aggiungi questa riga al tuo metodo, se stai usando un'API.

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); 

4

Questo tutorial è molto utile. Per dare un breve riepilogo:

  1. Utilizzare il pacchetto CORS disponibile su Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. Nel tuo WebApiConfig.csfile, aggiungi config.EnableCors()al Register()metodo.

  3. Aggiungi un attributo ai controller necessari per gestire cors:

[EnableCors(origins: "<origin address in here>", headers: "*", methods: "*")]


Ho dovuto utilizzare questo metodo perché avevo bisogno di impostare un'intestazione personalizzata nella mia richiesta e il metodo dell'attributo personalizzato non funzionava con la richiesta pre-volo del browser. Questo sembra funzionare in tutti i casi.
lehn0058,

3
    public ActionResult ActionName(string ReqParam1, string ReqParam2, string ReqParam3, string ReqParam4)
    {
        this.ControllerContext.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin","*");
         /*
                --Your code goes here --
         */
        return Json(new { ReturnData= "Data to be returned", Success=true }, JsonRequestBehavior.AllowGet);
    }

2

Esistono diversi modi in cui possiamo passare le intestazioni Access-Control-Expose-Headers.

  • Come ha spiegato jgauffin, possiamo creare un nuovo attributo.
  • Come ha spiegato LaundroMatt, possiamo aggiungere il file web.config.
  • Un altro modo è che possiamo aggiungere il codice come sotto nel file webApiconfig.cs.

    config.EnableCors (new EnableCorsAttribute (" ", headers: " ", metodi: "*", exposHeaders: "TestHeaderToExpose") {SupportsCredentials = true});

Oppure possiamo aggiungere il codice seguente nel file Global.Asax.

protected void Application_BeginRequest()
        {
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                //These headers are handling the "pre-flight" OPTIONS call sent by the browser
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:4200");
                HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers", "TestHeaderToExpose");
                HttpContext.Current.Response.End();
            }
        }

L'ho scritto per le opzioni. Modifica lo stesso secondo le tue necessità.

Buona programmazione !!


1

Dopo aver lottato per un'intera serata, ho finalmente fatto funzionare tutto questo. Dopo un po 'di debug ho scoperto che il problema che stavo affrontando era che il mio client stava inviando una cosiddetta richiesta di opzioni di verifica preliminare per verificare se all'applicazione era consentito inviare una richiesta di post con l'origine, i metodi e le intestazioni fornite. Non volevo usare Owin o un APIController, quindi ho iniziato a scavare e ho trovato la seguente soluzione con solo un ActionFilterAttribute. Soprattutto la parte "Accesso-Controllo-Consenti-Intestazioni" è molto importante, poiché le intestazioni qui menzionate devono corrispondere alle intestazioni inviate dalla richiesta.

using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyNamespace
{
    public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpRequest request = HttpContext.Current.Request;
            HttpResponse response = HttpContext.Current.Response;

            // check for preflight request
            if (request.Headers.AllKeys.Contains("Origin") && request.HttpMethod == "OPTIONS")
            {
                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                response.End();
            }
            else
            {
                HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                HttpContext.Current.Response.Cache.SetNoStore();

                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                if (request.HttpMethod == "POST")
                {
                    response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                    response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                }

                base.OnActionExecuting(filterContext);
            }
        }
    }
}

Infine, il mio metodo di azione MVC è simile al seguente. Importante qui è anche menzionare le Opzioni HttpVerbs, perché altrimenti la richiesta di verifica preliminare fallirà.

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Options)]
[AllowCrossSiteJson]
public async Task<ActionResult> Create(MyModel model)
{
    return Json(await DoSomething(model));
}

0

In Web.config immettere quanto segue

<system.webServer>
<httpProtocol>
  <customHeaders>
    <clear />     
    <add name="Access-Control-Allow-Credentials" value="true" />
    <add name="Access-Control-Allow-Origin" value="http://localhost:123456(etc)" />
  </customHeaders>
</httpProtocol>

0

Se usi IIS, suggerirei di provare il modulo IIS CORS .
È facile da configurare e funziona per tutti i tipi di controller.

Ecco un esempio di configurazione:

    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="*" />
            <add origin="https://*.microsoft.com"
                 allowCredentials="true"
                 maxAge="120"> 
                <allowHeaders allowAllRequestedHeaders="true">
                    <add header="header1" />
                    <add header="header2" />
                </allowHeaders>
                <allowMethods>
                     <add method="DELETE" />
                </allowMethods>
                <exposeHeaders>
                    <add header="header1" />
                    <add header="header2" />
                </exposeHeaders>
            </add>
            <add origin="http://*" allowed="false" />
        </cors>
    </system.webServer>

0

Sto usando DotNet Core MVC e dopo aver combattuto per alcune ore con pacchetti nuget, Startup.cs, attributi e questo posto, ho semplicemente aggiunto questo all'azione MVC:

Response.Headers.Add("Access-Control-Allow-Origin", "*");

Mi rendo conto che è piuttosto goffo, ma è tutto ciò di cui avevo bisogno, e nient'altro voleva aggiungere quelle intestazioni. Spero che questo aiuti qualcun'altro!

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.