web-dev-qa-db-fra.com

Comment activer CORS dans ASP.net Core WebAPI

Ce que j'essaie de faire

J'ai une API Web Web principale ASP.Net principale hébergée sur un plan Azure Free (code source: https://github.com/killerrin/Portfolio-Backend ).

J'ai aussi un site Web client que je veux faire utiliser cette API. L'application cliente ne sera pas hébergée sur Azure, mais plutôt sur Github Pages ou sur un autre service d'hébergement Web auquel j'ai accès. Pour cette raison, les noms de domaine ne s'aligneront pas.

En regardant cela, je dois activer CORS du côté de l'API Web, mais j'ai tout essayé depuis plusieurs heures maintenant et cela refuse de fonctionner.

Comment j'ai la configuration du client C'est juste un simple client écrit en React.js. J'appelle les API par le biais de AJAX dans Jquery. Le site React fonctionne donc je sais que ce n'est pas ça. L'appel de l'API Jquery fonctionne comme je l'ai confirmé dans Attempt 1. Voici comment je passe les appels.

    var apiUrl = "http://andrewgodfroyportfolioapi.azurewebsites.net/api/Authentication";
    //alert(username + "|" + password + "|" + apiUrl);
    $.ajax({
        url: apiUrl,
        type: "POST",
        data: {
            username: username,
            password: password
        },
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (response) {
            var authenticatedUser = JSON.parse(response);
            //alert("Data Loaded: " + authenticatedUser);
            if (onComplete != null) {
                onComplete(authenticatedUser);
            }
        },
        error: function (xhr, status, error) {
            //alert(xhr.responseText);
            if (onComplete != null) {
                onComplete(xhr.responseText);
            }
        }
    });

Ce que j'ai essayé


Tentative 1 - La méthode 'appropriée'

https://docs.Microsoft.com/en-us/aspnet/core/security/cors

J'ai suivi ce didacticiel sur le site Web de Microsoft jusqu'à un T, en essayant les 3 options de l'activer globalement dans le fichier Startup.cs, de le configurer sur chaque contrôleur et de l'essayer pour chaque action.

En suivant cette méthode, le domaine croisé fonctionne, mais uniquement sur une seule action sur un seul contrôleur (POST au AccountController). Pour tout le reste, le middleware Microsoft.AspNetCore.Cors refuse de définir les en-têtes.

J'ai installé Microsoft.AspNetCore.Cors via NUGET et la version est 1.1.2

Voici comment je l'ai installé dans Startup.cs

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add Cors
        services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
        {
            builder.AllowAnyOrigin()
                   .AllowAnyMethod()
                   .AllowAnyHeader();
        }));

        // Add framework services.
        services.AddMvc();
        services.Configure<MvcOptions>(options =>
        {
            options.Filters.Add(new CorsAuthorizationFilterFactory("MyPolicy"));
        });

        ...
        ...
        ...
    }

    // This method gets called by the runtime. Use this method to configure 
    //the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        // Enable Cors
        app.UseCors("MyPolicy");

        //app.UseMvcWithDefaultRoute();
        app.UseMvc();

        ...
        ...
        ...
    }

Comme vous pouvez le constater, je fais tout ce qui est dit. J'ajoute Cors avant MVC les deux fois, et lorsque cela ne fonctionnait pas, j'ai essayé de mettre [EnableCors("MyPolicy")] sur chaque contrôleur de la même manière

[Route("api/[controller]")]
[EnableCors("MyPolicy")]
public class AdminController : Controller

Tentative 2 - Brute le forçant

https://andrewlock.net/adding-default-security-headers-in-asp-net-core/

Après plusieurs heures d’essais sur la tentative précédente, j’ai pensé que j’essayerais de la forcer brutalement en essayant de définir les en-têtes manuellement, en les forçant à être exécutées à chaque réponse. Je l’ai fait après ce tutoriel sur la façon d’ajouter manuellement des en-têtes à chaque réponse.

Ce sont les en-têtes que j'ai ajoutés

.AddCustomHeader("Access-Control-Allow-Origin", "*")
.AddCustomHeader("Access-Control-Allow-Methods", "*")
.AddCustomHeader("Access-Control-Allow-Headers", "*")
.AddCustomHeader("Access-Control-Max-Age", "86400")

Ce sont d'autres en-têtes que j'ai essayés et qui ont échoué

.AddCustomHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE")
.AddCustomHeader("Access-Control-Allow-Headers", "content-type, accept, X-PINGOTHER")
.AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Host, User-Agent, Accept, Accept: application/json, application/json, Accept-Language, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, Connection, Content-Type, Content-Type: application/json, Authorization, Connection, Origin, Referer")

Avec cette méthode, les en-têtes Cross Site sont correctement appliqués et s'affichent dans la console de développement et dans Postman. Le problème cependant, c'est que même s'il réussit le test Access-Control-Allow-Origin, le navigateur Web jette un blason sur (je crois) Access-Control-Allow-Headers en indiquant 415 (Unsupported Media Type)

Donc, la méthode de la force brute ne fonctionne pas non plus


Enfin

Quelqu'un a-t-il réussi à faire fonctionner cela et pourrait-il me donner un coup de main ou simplement me diriger dans la bonne direction?


EDIT

Donc, pour que les appels d'API puissent passer, j'ai dû arrêter d'utiliser JQuery et passer au format Pure Javascript XMLHttpRequestname__.

Tentative 1

J'ai réussi à faire fonctionner le Microsoft.AspNetCore.Cors en suivant la réponse de MindingData, sauf dans la méthode Configurename__, qui place le app.UseCors avant le app.UseMvc.

En outre, une fois mélangé avec la solution API Javascript options.AllowAnyOrigin() pour la prise en charge des caractères génériques, il a également commencé à fonctionner.

Tentative 2

J'ai donc réussi à faire fonctionner Attempt 2 (brute forcing) ... à la seule exception que le caractère générique pour Access-Control-Allow-Origin ne fonctionne pas et que je dois donc définir manuellement les domaines qui y ont accès.

Ce n’est évidemment pas idéal car je souhaite simplement que cette WebAPI soit largement ouverte à tout le monde, mais cela fonctionne au moins pour moi sur un site séparé, ce qui signifie que c’est un début

app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder()
    .AddDefaultSecurePolicy()
    .AddCustomHeader("Access-Control-Allow-Origin", "http://localhost:3000")
    .AddCustomHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE")
    .AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type, Authorization"));
110
killerrin

Comme vous avez une politique CORS très simple (autoriser toutes les demandes du domaine XXX), vous n'avez pas besoin de compliquer les choses. Essayez d’abord de procéder comme suit (une implémentation très basique de CORS).

Si vous ne l'avez pas déjà fait, installez le paquet Nuget CORS.

Install-Package Microsoft.AspNetCore.Cors

Dans la méthode ConfigureServices de votre startup.cs, ajoutez les services CORS.

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(); // Make sure you call this previous to AddMvc
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Ensuite, dans votre méthode Configure de votre startup.cs, ajoutez ce qui suit:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // Make sure you call this before calling app.UseMvc()
    app.UseCors(
        options => options.WithOrigins("http://example.com").AllowAnyMethod()
    );

    app.UseMvc();
}

Maintenant, essayez-le. Les stratégies sont pour quand vous voulez différentes stratégies pour différentes actions (par exemple différents hôtes ou différents en-têtes). Pour votre exemple simple, vous n'en avez vraiment pas besoin. Commencez avec cet exemple simple et Tweak comme vous devez à partir de là.

Lectures supplémentaires: http://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/

159
MindingData
  • Dans ConfigureServices , ajoutez services.AddCors(); AVANT services.AddMvc ();
  • Ajouter des UseCors dans Configurez

    app.UseCors(builder => builder
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials());   
    app.UseMvc();
    

Le point principal est que ajoutez app.UseCors, avant app.UseMvc().

Assurez-vous de déclarer la fonctionnalité CORS avant MVC afin que le middleware se déclenche avant que le pipeline MVC ne prenne le contrôle et met fin à la demande.

Une fois que la méthode ci-dessus fonctionne, vous pouvez modifier la configuration d'une origine spécifique pour qu'elle accepte les appels d'API et évite de laisser votre API si ouverte à quiconque.

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
    {
        builder.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader();
    }));
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Dans la méthode configure, indiquez à CORS d'utiliser la stratégie que vous venez de créer:

app.UseCors("ApiCorsPolicy");
app.UseMvc();

Je viens de trouver cet article compact sur le sujet - https://dzone.com/articles/cors-in-net-core-net-core-security-part-vi

146
Ji Ra

J'ai créé ma propre classe de middleware qui a fonctionné pour moi. Je pense que quelque chose ne va pas avec la classe de middleware .net core

public class CorsMiddleware
{
    private readonly RequestDelegate _next;

    public CorsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public Task Invoke(HttpContext httpContext)
    {
        httpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
        httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
        httpContext.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
        httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
        return _next(httpContext);
    }
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class CorsMiddlewareExtensions
{
    public static IApplicationBuilder UseCorsMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CorsMiddleware>();
    }
}

et utilisé de cette façon dans le startup.cs

app.UseCorsMiddleware();
22
user8266077

Dans mon cas, seule la requête get fonctionne bien selon la réponse de MindingData. Pour les autres types de demande, vous devez écrire:

app.UseCors(corsPolicyBuilder =>
   corsPolicyBuilder.WithOrigins("http://localhost:3000")
  .AllowAnyMethod()
  .AllowAnyHeader()
);

N'oubliez pas d'ajouter .AllowAnyHeader()

14
Towhid

Pour développer ser8266077 's answer , j'ai toujours besoin de fournir une réponse OPTIONS pour demandes de contrôle en amont dans .NET Core 2.1-preview for mon cas d'utilisation:

// https://stackoverflow.com/a/45844400
public class CorsMiddleware
{
  private readonly RequestDelegate _next;

  public CorsMiddleware(RequestDelegate next)
  {
    _next = next;
  }

  public async Task Invoke(HttpContext context)
  {
    context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
    context.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
    // Added "Accept-Encoding" to this list
    context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Accept-Encoding, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
    context.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
    // New Code Starts here
    if (context.Request.Method == "OPTIONS")
    {
      context.Response.StatusCode = (int)HttpStatusCode.OK;
      await context.Response.WriteAsync(string.Empty);
    }
    // New Code Ends here

    await _next(context);
  }
}

puis activé le middleware comme dans Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  app.UseMiddleware(typeof(CorsMiddleware));
  // ... other middleware inclusion such as ErrorHandling, Caching, etc
  app.UseMvc();
}
8
ckr

Aucune des procédures ci-dessus n'a aidé et j'ai alors lu article qui résolvait le problème.

Ci-dessous le code.

public void ConfigureServices(IServiceCollection services)
{
    // Add service and create Policy with options
    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials() );
    });


    services.AddMvc(); 
}

et

public void Configure(IApplicationBuilder app)
{
    // ...

    // global policy - assign here or on each controller
    app.UseCors("CorsPolicy");

et au sommet de ma méthode d'action

[EnableCors("CorsPolicy")]
5
Sainath

essayez d'ajouter jQuery.support.cors = true; avant l'appel Ajax

Il se peut également que les données que vous envoyez à l’API soient erronées,

essayez d'ajouter la fonction JSON suivante

        var JSON = JSON || {};

    // implement JSON.stringify serialization
    JSON.stringify = JSON.stringify || function (obj) {

        var t = typeof (obj);
        if (t != "object" || obj === null) {

            // simple data type
            if (t == "string") obj = '"' + obj + '"';
            return String(obj);

        }
        else {

            // recurse array or object
            var n, v, json = [], arr = (obj && obj.constructor == Array);

            for (n in obj) {
                v = obj[n]; t = typeof (v);

                if (t == "string") v = '"' + v + '"';
                else if (t == "object" && v !== null) v = JSON.stringify(v);

                json.Push((arr ? "" : '"' + n + '":') + String(v));
            }

            return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
        }
    };

    // implement JSON.parse de-serialization
    JSON.parse = JSON.parse || function (str) {
        if (str === "") str = '""';
        eval("var p=" + str + ";");
        return p;
    };

puis dans vos données: objet le changer en

    data: JSON.stringify({
        username: username,
        password: password
    }),
4
Riaan Saayman

D'après votre commentaire dans la réponse de MindingData, cela n'a rien à voir avec votre CORS, cela fonctionne bien.

Votre action du contrôleur renvoie les mauvaises données. HttpCode 415 signifie "Type de support non supporté". Cela se produit lorsque vous transmettez le mauvais format au contrôleur (XML, par exemple, à un contrôleur qui accepte uniquement JSON) ou lorsque vous renvoyez un type incorrect (renvoyez Xml dans un contrôleur déclaré pour ne renvoyer que xml).

Pour plus tard, vérifiez l'existence de [Produces("...")]attribute sur votre action

2
Tseng

Dans launchSettings.json, sous iisSettings, définissez anonymousAuthentication sur true:

"iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:4200/",
      "sslPort": 0
    }
  }

Ensuite, dans Startup.cs, sous ConfigureServices, avant services.AddMvc, ajoutez:

services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
{
    builder
        .AllowAnyOrigin()
        .WithHeaders(HeaderNames.AccessControlAllowHeaders, "Content-Type")
        .AllowAnyMethod()
        .AllowCredentials();
}));

puis dans la méthode configure, avant app.UseMvc (), ajoutez:

app.UseCors("ApiCorsPolicy");
1
Adrian

Je pense que si vous utilisez votre propre middleware CORS, vous devez vous assurer qu'il s'agit bien de la requête CORS en vérifiant Origine en-tête.

 public class CorsMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IMemoryCache _cache;
    private readonly ILogger<CorsMiddleware> _logger;

    public CorsMiddleware(RequestDelegate next, IMemoryCache cache, ILogger<CorsMiddleware> logger)
    {
        _next = next;
        _cache = cache;
        _logger = logger;
    }
    public async Task InvokeAsync(HttpContext context, IAdministrationApi adminApi)
    {
        if (context.Request.Headers.ContainsKey(CorsConstants.Origin) || context.Request.Headers.ContainsKey("Origin"))
        {
            if (!context.Request.Headers.TryGetValue(CorsConstants.Origin, out var Origin))
            {
                context.Request.Headers.TryGetValue("Origin", out Origin);
            }

            bool isAllowed;
            // Getting Origin from DB to check with one from request and save it in cache 
            var result = _cache.GetOrCreateAsync(Origin, async cacheEntry => await adminApi.DoesExistAsync(Origin));
            isAllowed = result.Result.Result;

            if (isAllowed)
            {
                context.Response.Headers.Add(CorsConstants.AccessControlAllowOrigin, Origin);
                context.Response.Headers.Add(
                    CorsConstants.AccessControlAllowHeaders,
                    $"{HeaderNames.Authorization}, {HeaderNames.ContentType}, {HeaderNames.AcceptLanguage}, {HeaderNames.Accept}");
                context.Response.Headers.Add(CorsConstants.AccessControlAllowMethods, "POST, GET, PUT, PATCH, DELETE, OPTIONS");

                if (context.Request.Method == "OPTIONS")
                {
                    _logger.LogInformation("CORS with Origin {Origin} was handled successfully", Origin);
                    context.Response.StatusCode = (int)HttpStatusCode.NoContent;
                    return;
                }

                await _next(context);
            }
            else
            {
                if (context.Request.Method == "OPTIONS")
                {
                    _logger.LogInformation("Preflight CORS request with Origin {Origin} was declined", Origin);
                    context.Response.StatusCode = (int)HttpStatusCode.NoContent;
                    return;
                }

                _logger.LogInformation("Simple CORS request with Origin {Origin} was declined", Origin);
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }
        }

        await _next(context);
    }
1
Riddik

Pour moi, cela n'avait rien à voir avec le code que j'utilisais. Pour Azure, nous devions entrer dans les paramètres de App Service, dans le menu latéral, l'entrée "CORS". Là je devais ajouter le domaine que je demandais des choses. Une fois que j'ai eu ça, tout était magique.

1
kjbetz

La solution la plus simple est d'ajouter

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseCors(options => options.AllowAnyOrigin());

        app.UseHttpsRedirection();
        app.UseMvc();
    }

à Startup.cs.

0
amilamad

La réponse ci-dessus de MindingData fonctionnait bien, mais je devais utiliser Microsoft.AspNet.Cors au lieu de Microsoft.AspNetCore.Cors. J'utilise un projet d'API d'application Web .NetCore dans Visual Studio 2019

0
carl