origin net mvc enable control classic aspnet asp allow all c# rest asp.net-core cors cross-domain

c# - mvc - enable cors web api.net core 2



Cómo habilitar CORS en ASP.net Core WebAPI (12)

Lo que estoy tratando de hacer

Tengo una API web ASP.Net Core de back-end alojada en un plan gratuito de Azure (código fuente: https://github.com/killerrin/Portfolio-Backend ).

También tengo un sitio web de cliente que quiero hacer que consuma esa API. La aplicación del cliente no se alojará en Azure, sino que se alojará en las páginas de Github o en otro servicio de alojamiento web al que tenga acceso. Debido a esto, los nombres de dominio no se alinearán.

Al investigar esto, necesito habilitar CORS en el lado de la API web, sin embargo, he intentado casi todo durante varias horas y se niega a funcionar.

Cómo tengo la configuración del cliente Es solo un cliente simple escrito en React.js. Llamo a las API a través de AJAX en Jquery. El sitio React funciona, así que sé que no es eso. La llamada a la API de Jquery funciona como lo confirmé en el intento 1. Así es como hago las llamadas

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

Lo que he intentado

Intento 1 - La forma ''adecuada''

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

He seguido este tutorial en el sitio web de Microsoft hasta T, probando las 3 opciones de habilitarlo globalmente en Startup.cs, configurándolo en cada controlador y probándolo en cada acción.

Siguiendo este método, Cross Domain funciona, pero solo en una sola Acción en un solo controlador (POST al AccountController). Para todo lo demás, el middleware Microsoft.AspNetCore.Cors niega a configurar los encabezados.

Instalé Microsoft.AspNetCore.Cors través de NUGET y la versión es 1.1.2

Así es como lo configuro en 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(); ... ... ... }

Como puede ver, estoy haciendo todo lo que me dijeron. Agregué Cors antes de MVC las dos veces, y cuando eso no funcionó intenté poner [EnableCors("MyPolicy")] en cada controlador como tal

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

Intento 2 - Brute forzándolo

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

Después de varias horas de intentar el intento anterior, pensé que trataría de aplicar una fuerza bruta tratando de configurar los encabezados manualmente, obligándolos a ejecutar cada respuesta. Hice esto siguiendo este tutorial sobre cómo agregar encabezados manualmente a cada respuesta.

Estos son los encabezados que agregué

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

Estos son otros encabezados que probé que fallaron

.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")

Con este método, los encabezados Cross Site se aplican correctamente y se muestran en mi consola de desarrollador y en Postman. Sin embargo, el problema es que mientras pasa la verificación de Access-Control-Allow-Origin , el navegador web lanza un ataque siseante (creo) Access-Control-Allow-Headers indica 415 (Unsupported Media Type)

Entonces el método de fuerza bruta tampoco funciona

Finalmente

¿Alguien ha conseguido que esto funcione y pueda echar una mano, o simplemente poder señalarme en la dirección correcta?

EDITAR

Entonces, para que las llamadas a la API se realicen, tuve que dejar de usar JQuery y cambiar a un formato Pure Javascript XMLHttpRequest .

Intento 1

Logré que Microsoft.AspNetCore.Cors funcione siguiendo la respuesta de MindingData, excepto dentro del Método de Configure coloca la app.UseCors antes de app.UseMvc .

Además, cuando se combina con las options.AllowAnyOrigin() solución de la API de Javascript, options.AllowAnyOrigin() para la compatibilidad con comodines también comenzó a funcionar.

Intento 2

Así que he logrado que el Intento 2 (fuerza bruta) funcione ... con la única excepción de que el Comodín para Access-Control-Allow-Origin no funciona y, como tal, tengo que configurar manualmente los dominios que tienen acceso lo.

Obviamente no es ideal, ya que solo quiero que este WebAPI esté abierto para todos, pero al menos funciona para mí en un sitio separado, lo que significa que es un comienzo

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


Creé mi propia clase de middleware que funcionó para mí, creo que hay algo mal con la clase 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>(); } }

y lo usé de esta manera en el startup.cs

app.UseCorsMiddleware();


Creo que si usa su propio middleware CORS , debe asegurarse de que realmente sea una solicitud CORS marcando el encabezado de origen .

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


Debido a que tiene una política CORS muy simple (Permitir todas las solicitudes del dominio XXX), no necesita hacerla tan complicada. Intente hacer lo siguiente primero (Una implementación muy básica de CORS).

Si aún no lo ha hecho, instale el paquete nuget CORS.

Install-Package Microsoft.AspNetCore.Cors

En el método ConfigureServices de su startup.cs, agregue los servicios CORS.

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

Luego, en el método Configure de su startup.cs, agregue lo siguiente:

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

Ahora inténtalo. Las políticas son para cuando desea diferentes políticas para diferentes acciones (por ejemplo, diferentes hosts o diferentes encabezados). Por su simple ejemplo, realmente no lo necesita. Comience con este sencillo ejemplo y modifique como lo necesite desde allí.

Más información: http://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/


En mi caso, solo get solicitud funciona bien de acuerdo con la respuesta de MindingData. Para otros tipos de solicitud, debe escribir:

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

No olvides agregar .AllowAnyHeader()


La solución más simple es agregar

public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseCors(options => options.AllowAnyOrigin()); app.UseHttpsRedirection(); app.UseMvc(); }

a Startup.cs.


Ninguno de los procedimientos anteriores ayudó y luego leí el article que resolvió el problema.

Debajo está el código.

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

y

public void Configure(IApplicationBuilder app) { // ... // global policy - assign here or on each controller app.UseCors("CorsPolicy");

y en la parte superior de mi método de acción

[EnableCors("CorsPolicy")]


Obtuve la respuesta anterior de MindingData para trabajar, pero tuve que usar Microsoft.AspNet.Cors en lugar de Microsoft.AspNetCore.Cors. Estoy usando el proyecto de API de aplicación web .NetCore en Visual Studio 2019


Para ampliar la answer , descubrí que todavía necesitaba proporcionar la respuesta OPTIONS para las solicitudes de verificación previa en .NET Core 2.1-preview para mi caso de uso:

// https://.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); } }

y luego habilitó el middleware como en Startup.cs

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


Para mí, no tenía nada que ver con el código que estaba usando. Para Azure tuvimos que ir a la configuración del Servicio de aplicaciones, en el menú lateral, la entrada "CORS". Allí tuve que agregar el dominio del que estaba solicitando cosas. Una vez que tuve eso, todo fue mágico.


Según su comentario en la respuesta de MindingData, no tiene nada que ver con su CORS, está funcionando bien.

Su acción de controlador está devolviendo los datos incorrectos. HttpCode 415 significa "Tipo de medio no compatible". Esto sucede cuando pasa el formato incorrecto al controlador (es decir, XML a un controlador que solo acepta json) o cuando devuelve un tipo incorrecto (devuelve Xml en un controlador que se declara que solo devuelve xml).

Para más adelante, verifique la existencia del atributo [Produces("...")] en su acción


intente agregar jQuery.support.cors = true; antes de la llamada de Ajax

También podría ser que los datos que envía a la API son inestables,

intente agregar la siguiente función JSON

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

luego en sus datos: objeto cambiarlo a

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


  • En ConfigureServices agregue services.AddCors(); ANTES de services.AddMvc ();
  • Añadir UseCors en Configurar

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

El punto principal es que agrega app.UseCors , antes de app.UseMvc() .

Asegúrese de declarar la funcionalidad CORS antes de MVC para que el middleware se active antes de que la tubería MVC obtenga el control y finalice la solicitud.

Después de que el método anterior funcione, puede cambiarlo, configurar un ORIGEN específico para aceptar llamadas API y evitar dejar su API tan abierta a cualquiera

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

En el método de configuración, dígale a CORS que use la política que acaba de crear:

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

Acabo de encontrar este artículo compacto sobre el tema: https://dzone.com/articles/cors-in-net-core-net-core-security-part-vi