Définition d'Access-Control-Allow-Origin dans ASP.Net MVC - méthode la plus simple possible

206

J'ai une méthode d'action simple, qui renvoie du json. Il fonctionne sur ajax.example.com. J'ai besoin d'y accéder à partir d'un autre site someothersite.com.

Si j'essaye de l'appeler, j'obtiens l'attendu ...:

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

Je connais deux façons de contourner cela: JSONP et la création d'un HttpHandler personnalisé pour définir l'en-tête.

N'y a-t-il pas de moyen plus simple?

N'est-il pas possible pour une action simple de définir une liste des origines autorisées - ou simplement d'autoriser tout le monde? Peut-être un filtre d'action?

L'optimal serait ...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);
Kjensen
la source
1
Veuillez consulter ici vNext et MVC6: neelbhatt40.wordpress.com/2015/09/10/…
Neel

Réponses:

382

Pour les contrôleurs ASP.NET MVC simples

Créer un nouvel attribut

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

Marquez votre action:

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

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

Balisez un contrôleur API entier:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

Ou des appels API individuels:

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

Pour Internet Explorer <= v9

IE <= 9 ne prend pas en charge CORS. J'ai écrit un javascript qui acheminera automatiquement ces demandes via un proxy. Tout est 100% transparent (il suffit d'inclure mon proxy et le script).

Téléchargez-le à l'aide de nuget corsproxyet suivez les instructions incluses.

Article de blog | Code source

jgauffin
la source
8
Incroyable! J'adore MVC + U!
Piotr Kula
2
en admiration devant l'élégance de cette solution
BraveNewMath
3
Vous pouvez facilement étendre l'attribut pour accepter une origine spécifique si vous souhaitez limiter CORS à vos propres domaines.
Petrus Theron
2
Vous devriez pouvoir l'ajouter aux RegisterHttpFilters dans votre App_Start \ FilterConfig correct? Cela s'appliquerait à tous les contrôleurs Api de votre projet. En couplant cela avec le commentaire de Pate ci-dessus, vous pouvez limiter CORS à vos domaines pour tous les contrôleurs.
bdwakefield
9
J'ai récemment mis à jour notre projet vers MVC 5 et j'ai tenté de le faire. Même l'ajout de l'en-tête dans un filtre ne semble pas fonctionner. Lorsque je vois la demande dans le réseau, l'en-tête n'est pas présent sur la réponse. Y a-t-il autre chose qui doit être fait pour que cela fonctionne?
Kneemin
121

Si vous utilisez IIS 7+, vous pouvez placer un fichier web.config à la racine du dossier avec celui-ci dans la section system.webServer:

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

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

LaundroMatt
la source
1
Je ne me souviens plus pourquoi, mais cette méthode ne fonctionne pas toujours dans IIS 7+
LaundroMatt
Hmm. La seule raison pour laquelle je peux penser que cela ne fonctionnerait pas est si une demande provient d'un navigateur non CORS. Mais je vais continuer à enquêter.
sellmeadog
29
En outre, cela rendrait l'ensemble du site Web compatible avec CORS. Si quelqu'un veut ne marquer qu'une seule action ou contrôleur comme CORS-friendly, alors la réponse acceptée est bien meilleure.
Lev Dubinets
1
Si vous voyez la section ASP.Net , elle a un indice : "Remarque: cette approche est compatible avec IIS6, IIS7 Classic Mode et IIS7 Integrated Mode."
percebus
1
Je fais face à un problème interdomaine lorsque je publie mon application sur l'environnement SharePoint. Lorsque j'exécute mon application sur l'environnement local, mon application fonctionne correctement, mais lorsque je la publie sur azur sur mon site sharepoint, elle redirige vers la page d'erreur lors de l'appel du formulaire Ajax.Begin. J'ai essayé cette solution mais cela ne fonctionne pas pour moi. Y a-t-il une autre alternative?
Jyotsna Wadhwani
22

Je suis tombé sur un problème où le navigateur refusait de diffuser le contenu qu'il avait récupéré lorsque la demande était passée dans les cookies (par exemple, le xhr avait son withCredentials=true), et le site s'était Access-Control-Allow-Originmis à *. (L'erreur dans Chrome était "Impossible d'utiliser le caractère générique dans Access-Control-Allow-Origin lorsque l'indicateur d'informations d'identification est vrai.")

Sur la base de la réponse de @jgauffin, j'ai créé ceci, qui est essentiellement une façon de contourner ce contrôle de sécurité du navigateur particulier, donc mettez en garde 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);
    }
}
Ken Smith
la source
Cela a été particulièrement utile, merci.
cklimowski
15

C'est vraiment simple, il suffit d'ajouter ceci dans 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>

Dans Origin, placez tous les domaines ayant accès à votre serveur Web, dans Headers, mettez tous les en-têtes possibles que toute demande Ajax http peut utiliser, dans les méthodes, mettez toutes les méthodes que vous autorisez sur votre serveur.

Cordialement :)

Zvonimir Tokic
la source
L'ajout d '"autorisation" dans Access-Control-Allow-Headers peut également être utile si vous avez l'intention d'utiliser des requêtes autorisées.
AFract
9

Parfois, le verbe OPTIONS cause aussi des problèmes

Simplement: mettez à jour votre web.config avec ce qui suit

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

Et mettez à jour les en-têtes de service Web / contrôleur avec httpGet et httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {
Bishoy Hanna
la source
BTW, dans sitefinity, vous devez ajouter * aux paramètres avancés du système dans la section sécurité
Bishoy Hanna
quels sont les fichiers dans lesquels j'ai besoin de mettre à jour les en-têtes du contrôleur?
user3281466
5

Ajoutez cette ligne à votre méthode, si vous utilisez une API.

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

Ce tutoriel est très utile. Pour résumer brièvement:

  1. Utilisez le package CORS disponible sur Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. Dans votre WebApiConfig.csfichier, ajoutez config.EnableCors()à la Register()méthode.

  3. Ajoutez un attribut aux contrôleurs dont vous avez besoin pour gérer les cors:

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

GrandMasterFlush
la source
J'ai dû utiliser cette méthode car j'avais besoin de définir un en-tête personnalisé dans ma demande, et la méthode d'attribut personnalisé ne fonctionnait pas avec la demande de pré-vol du navigateur. Cela semble fonctionner dans tous les cas.
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);
    }
Pranav Labhe
la source
2

Il existe différentes façons de passer les en-têtes Access-Control-Expose-Headers.

  • Comme jgauffin l'a expliqué, nous pouvons créer un nouvel attribut.
  • Comme LaundroMatt l'a expliqué, nous pouvons ajouter le fichier web.config.
  • Une autre façon consiste à ajouter du code comme ci-dessous dans le fichier webApiconfig.cs.

    config.EnableCors (nouveau EnableCorsAttribute (" ", en-têtes: " ", méthodes: "*", exposésHeaders: "TestHeaderToExpose") {SupportsCredentials = true});

Ou nous pouvons ajouter le code ci-dessous dans le fichier 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();
            }
        }

Je l'ai écrit pour les options. Veuillez modifier les mêmes selon vos besoins.

Codage heureux !!

Trilok Pathak
la source
1

Après avoir lutté pendant toute une soirée, j'ai finalement réussi à le faire fonctionner. Après un débogage, j'ai trouvé que le problème que je rencontrais était que mon client envoyait une soi-disant demande d'options de contrôle en amont pour vérifier si l'application était autorisée à envoyer une demande de publication avec l'origine, les méthodes et les en-têtes fournis. Je ne voulais pas utiliser Owin ou un APIController, j'ai donc commencé à creuser et j'ai trouvé la solution suivante avec juste un ActionFilterAttribute. En particulier, la partie "Access-Control-Allow-Headers" est très importante, car les en-têtes mentionnés doivent correspondre aux en-têtes que votre demande enverra.

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

Enfin, ma méthode d'action MVC ressemble à ceci. Il est important ici de mentionner également les options HttpVerbs, car sinon la demande de contrôle en amont échouera.

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

Dans Web.config, entrez ce qui suit

<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>
Elvis Skensberg
la source
0

Si vous utilisez IIS, je vous suggère d'essayer le module IIS CORS .
Il est facile à configurer et fonctionne pour tous les types de contrôleurs.

Voici un exemple de configuration:

    <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>
olsh
la source
0

J'utilise DotNet Core MVC et après avoir combattu pendant quelques heures avec des packages de nuget, Startup.cs, des attributs et cet endroit, j'ai simplement ajouté ceci à l'action MVC:

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

Je me rends compte que c'est assez maladroit, mais c'est tout ce dont j'avais besoin, et rien d'autre ne voulait ajouter ces en-têtes. J'espère que ça aidera quelqu'un d'autre!

Ben Power
la source