Habilitar solicitudes entre orígenes (CORS) en ASP.NET Core

Por Rick Anderson y Kirk Larkin

En este artículo se muestra cómo habilitar CORS en una ASP.NET Core aplicación.

La seguridad del explorador evita que una página web realice solicitudes a un dominio diferente del que atendió a dicha página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen evita que un sitio malintencionado lea información confidencial de otro sitio. A veces, es posible que quiera permitir que otros sitios realicen solicitudes entre orígenes a la aplicación. Para obtener más información, consulte el artículo de Mozilla CORS.

Uso compartido de recursos entre orígenes (CORS):

  • Es un estándar W3C que permite que un servidor relaje la directiva del mismo origen.
  • No es una característica de seguridad, CORS relaja la seguridad. Una API no es más segura al permitir CORS. Para obtener más información, vea Funcionamiento de CORS.
  • Permite que un servidor permita explícitamente algunas solicitudes entre orígenes mientras rechaza otras.
  • Es más seguro y flexible que las técnicas anteriores, como JSONP.

Vea o descargue el código de ejemplo (cómo descargarlo)

Mismo origen

Dos direcciones URL tienen el mismo origen si tienen esquemas, hosts y puertos idénticos(RFC 6454).

Estas dos direcciones URL tienen el mismo origen:

  • https://example.com/foo.html
  • https://example.com/bar.html

Estas direcciones URL tienen orígenes diferentes a los de las dos direcciones URL anteriores:

  • https://example.net: dominio diferente
  • https://www.example.com/foo.html: subdominio diferente
  • http://example.com/foo.html: esquema diferente
  • https://example.com:9000/foo.html: puerto diferente

Habilitación de CORS

Hay tres maneras de habilitar CORS:

El uso del atributo [EnableCors] con una directiva con nombre proporciona el control más preciso para limitar los puntos de conexión que admiten CORS.

Advertencia

UseCors debe llamarse en el orden correcto. Para obtener más información, vea Orden de middleware. Por ejemplo, UseCors se debe llamar a antes de cuando se usa UseResponseCaching UseResponseCaching .

Cada enfoque se detalla en las secciones siguientes.

CORS con directiva con nombre y middleware

El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva CORS a todos los puntos de conexión de la aplicación con los orígenes especificados:

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

El código anterior:

  • Establece el nombre de la directiva en _myAllowSpecificOrigins . El nombre de la directiva es arbitrario.
  • Llama al UseCors método de extensión y especifica la directiva _myAllowSpecificOrigins CORS. UseCors agrega el middleware cors. La llamada a UseCors debe colocarse después de UseRouting , pero antes de UseAuthorization . Para obtener más información, vea Orden de middleware.
  • Llama AddCors a con una expresión lambda. La expresión lambda toma un CorsPolicyBuilder objeto . Las opciones deconfiguración, como WithOrigins , se describen más adelante en este artículo.
  • Habilita la _myAllowSpecificOrigins directiva CORS para todos los puntos de conexión del controlador. Consulte Enrutamiento de puntos de conexión para aplicar una directiva de CORS a puntos de conexión específicos.
  • Cuando se usa middleware de almacenamiento en caché de respuestas,llame a antes de UseCors UseResponseCaching .

Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting y UseEndpoints .

Consulte Test CORS (Probar CORS) para obtener instrucciones sobre cómo probar código similar al código anterior.

La AddCors llamada al método agrega servicios CORS al contenedor de servicios de la aplicación:

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Para más información, consulte Opciones de directiva de CORS en este documento.

Los CorsPolicyBuilder métodos se pueden encadenar, como se muestra en el código siguiente:

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
});

builder.Services.AddControllers();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Nota: La dirección URL especificada no debe contener una barra diagonal final ( / ). Si la dirección URL finaliza con / , la comparación devuelve y no se devuelve ningún false encabezado.

CORS con directiva predeterminada y middleware

El código resaltado siguiente habilita la directiva CORS predeterminada:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(
        builder =>
        {
            builder.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

El código anterior aplica la directiva CORS predeterminada a todos los puntos de conexión del controlador.

Habilitación de CORS con enrutamiento de punto de conexión

La habilitación de CORS por punto de conexión mediante no admite solicitudes de comprobación RequireCors previa automáticas. Para obtener más información, vea este GitHub y Prueba de CORS con enrutamiento de puntos de conexión y [HttpOptions].

Con el enrutamiento de puntos de conexión, CORS se puede habilitar por punto de conexión mediante el RequireCors conjunto de métodos de extensión:

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/echo",
        context => context.Response.WriteAsync("echo"))
        .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapControllers()
             .RequireCors(MyAllowSpecificOrigins);

    endpoints.MapGet("/echo2",
        context => context.Response.WriteAsync("echo2"));

    endpoints.MapRazorPages();
});

app.Run();

En el código anterior:

  • app.UseCors habilita el middleware de CORS. Dado que no se ha configurado una directiva predeterminada, app.UseCors() por sí sola no habilita CORS.
  • Los /echo puntos de conexión de controlador y permiten solicitudes entre orígenes mediante la directiva especificada.
  • Los /echo2 puntos de conexión y Pages no Razor permiten solicitudes entre orígenes porque no se especificó ninguna directiva predeterminada.

El atributo [DisableCors] no deshabilita CORS habilitado por el enrutamiento de puntos de conexión con RequireCors .

Consulte Test CORS with endpoint routing (Probar CORS con enrutamiento de puntos de conexión) y [HttpOptions] para obtener instrucciones sobre cómo probar código similar al anterior.

Habilitación de CORS con atributos

Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporciona el control más preciso.

El atributo [EnableCors] proporciona una alternativa a la aplicación global de CORS. El [EnableCors] atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:

  • [EnableCors] especifica la directiva predeterminada.
  • [EnableCors("{Policy String}")] especifica una directiva con nombre.

El [EnableCors] atributo se puede aplicar a:

  • Razor Página PageModel
  • Controller
  • Método de acción del controlador

Se pueden aplicar directivas diferentes a controladores, modelos de página o métodos de acción con el [EnableCors] atributo . Cuando el atributo se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican [EnableCors] ambas directivas. Se recomienda no combinar directivas. Use el [EnableCors] atributo o middleware, no ambos en la misma aplicación.

El código siguiente aplica una directiva diferente a cada método:

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

El código siguiente crea dos directivas CORS:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("Policy1",
        builder =>
        {
            builder.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });

    options.AddPolicy("AnotherPolicy",
        builder =>
        {
            builder.WithOrigins("http://www.contoso.com")
                                .AllowAnyHeader()
                                .AllowAnyMethod();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Para el control más preciso de la limitación de las solicitudes de CORS:

  • Use [EnableCors("MyPolicy")] con una directiva con nombre.
  • No defina una directiva predeterminada.
  • No use el enrutamiento de puntos de conexión.

El código de la sección siguiente cumple la lista anterior.

Consulte Test CORS (Probar CORS) para obtener instrucciones sobre cómo probar código similar al código anterior.

Deshabilitar CORS

El atributo [DisableCors] no deshabilita CORS habilitado por el enrutamiento de puntos de conexión.

El código siguiente define la directiva cors "MyPolicy" :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
        builder =>
        {
            builder.WithOrigins("http://example.com",
                                "http://www.contoso.com")
                    .WithMethods("PUT", "DELETE", "GET");
        });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

El código siguiente deshabilita CORS para la GetValues2 acción:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

El código anterior:

Consulte Test CORS (Probar CORS) para obtener instrucciones sobre cómo probar el código anterior.

Opciones de directiva de CORS

En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:

AddPolicyse llama en Program.cs. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS.

Establecer los orígenes permitidos

AllowAnyOrigin: permite solicitudes CORS desde todos los orígenes con cualquier esquema ( http o https ). AllowAnyOrigin no es seguro porque cualquier sitio web puede realizar solicitudes entre orígenes a la aplicación.

Nota

Especificar AllowAnyOrigin y AllowCredentials es una configuración no segura y puede dar lugar a una falsificación de solicitud entre sitios. El servicio CORS devuelve una respuesta CORS no válida cuando una aplicación está configurada con ambos métodos.

AllowAnyOrigin afecta a las solicitudes de nivel previo y al Access-Control-Allow-Origin encabezado . Para obtener más información, consulte la sección Solicitudes de comprobación previa.

SetIsOriginAllowedToAllowWildcardSubdomains: establece la propiedad de la directiva para que sea una función que permita que los orígenes coincidan con un dominio con caracteres comodín configurado al evaluar si se permite IsOriginAllowed el origen.

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        builder =>
        {
            builder.WithOrigins("https://*.example.com")
                .SetIsOriginAllowedToAllowWildcardSubdomains();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Establecer los métodos HTTP permitidos

AllowAnyMethod:

  • Permite cualquier método HTTP:
  • Afecta a las solicitudes de nivel previo y al Access-Control-Allow-Methods encabezado. Para obtener más información, consulte la sección Solicitudes de comprobación previa.

Establecimiento de los encabezados de solicitud permitidos

Para permitir que se envíen encabezados específicos en una solicitud CORS, denominados encabezadosde solicitud de autor, llame a WithHeaders y especifique los encabezados permitidos:

using Microsoft.Net.Http.Headers;

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
       builder =>
       {
           builder.WithOrigins("http://example.com")
                  .WithHeaders(HeaderNames.ContentType, "x-custom-header");
       });
});

builder.Services.AddControllers();

var app = builder.Build();

Para permitir todos los encabezados de solicitud de autor,llame a AllowAnyHeader :

var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
        builder =>
        {
            builder.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

AllowAnyHeaderafecta a las solicitudes de nivel previo y al encabezado Access-Control-Request-Headers. Para obtener más información, consulte la sección Solicitudes de comprobación previa.

Una coincidencia de directiva de middleware de CORS con encabezados específicos especificados por solo es posible cuando los encabezados enviados en coinciden exactamente con los WithHeaders Access-Control-Request-Headers encabezados indicados en WithHeaders .

Por ejemplo, considere una aplicación configurada de la siguiente manera:

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

El middleware de CORS rechaza una solicitud de nivel previo con el siguiente encabezado de solicitud porque Content-Language (HeaderNames.ContentLanguage) no aparece en WithHeaders :

Access-Control-Request-Headers: Cache-Control, Content-Language

La aplicación devuelve una respuesta 200 OK, pero no devuelve los encabezados CORS. Por lo tanto, el explorador no intenta realizar la solicitud entre orígenes.

Establecer los encabezados de respuesta expuestos

De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, vea W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Los encabezados de respuesta que están disponibles de forma predeterminada son:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

La especificación cors llama a estos encabezados encabezados de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a WithExposedHeaders :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyExposeResponseHeadersPolicy",
        builder =>
        {
            builder.WithOrigins("https://*.example.com")
                   .WithExposedHeaders("x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Credenciales en solicitudes entre orígenes

Las credenciales requieren un control especial en una solicitud cors. De forma predeterminada, el explorador no envía credenciales con una solicitud entre orígenes. Las credenciales incluyen cookie s y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud entre orígenes, el cliente debe establecer XMLHttpRequest.withCredentials en true .

Usar XMLHttpRequest directamente:

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Uso de jQuery:

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Mediante fetch API:

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

El servidor debe permitir las credenciales. Para permitir credenciales entre orígenes, llame a AllowCredentials :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyMyAllowCredentialsPolicy",
        builder =>
        {
            builder.WithOrigins("http://example.com")
                   .AllowCredentials();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

La respuesta HTTP incluye un encabezado , que indica al explorador que el servidor permite Access-Control-Allow-Credentials credenciales para una solicitud entre orígenes.

Si el explorador envía credenciales pero la respuesta no incluye un encabezado válido, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud Access-Control-Allow-Credentials entre orígenes.

Permitir credenciales entre orígenes es un riesgo de seguridad. Un sitio web de otro dominio puede enviar las credenciales de un usuario que ha iniciado sesión a la aplicación en nombre del usuario sin que el usuario lo sepa.

La especificación de CORS también indica que establecer los orígenes en (todos los orígenes) no es válido "*" si el encabezado está Access-Control-Allow-Credentials presente.

Solicitudes de comprobación previa

Para algunas solicitudes cors, el explorador envía una solicitud OPTIONS adicional antes de realizar la solicitud real. Esta solicitud se denomina solicitud de comprobación previa. El explorador puede omitir la solicitud de comprobación previa si se cumplen todas las condiciones siguientes:

  • El método de solicitud es GET, HEAD o POST.
  • La aplicación no establece encabezados de solicitud distintos de Accept , , , o Accept-Language Content-Language Content-Type Last-Event-ID .
  • El Content-Type encabezado, si se establece, tiene uno de los valores siguientes:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La regla sobre los encabezados de solicitud establecidos para la solicitud de cliente se aplica a los encabezados que la aplicación establece mediante una setRequestHeader llamada a en el objeto XMLHttpRequest . La especificación de CORS llama a estos encabezados para crear encabezados de solicitud. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent Host , o Content-Length .

A continuación se muestra una respuesta de ejemplo similar a la solicitud previa realizada desde el botón [Put test] de la sección Test CORS (Probar CORS) de este documento.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

La solicitud de comprobación previa usa el método HTTP OPTIONS. Puede incluir los siguientes encabezados:

Si se deniega la solicitud de comprobación previa, la aplicación devuelve una 200 OK respuesta, pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta realizar la solicitud entre orígenes. Para obtener un ejemplo de una solicitud previa denegada, consulte la sección Prueba de CORS de este documento.

Con las herramientas de F12, la aplicación de consola muestra un error similar a uno de los siguientes, dependiendo del explorador:

  • Firefox: solicitud entre orígenes bloqueada: la misma directiva de origen no permite leer el recurso remoto en https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 . (Motivo: la solicitud de CORS no se ha hecho correctamente). Más información
  • Chromium basado en: la directiva CORS ha bloqueado el acceso para capturar en "" desde el origen": La respuesta a la solicitud de comprobación previa no pasa la comprobación de control de acceso: no hay ningún encabezado https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net "Access-Control-Allow-Origin" en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.

Para permitir encabezados específicos, llame a WithHeaders :

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowHeadersPolicy",
        builder =>
        {
        builder.WithOrigins("http://example.com")
                   .WithHeaders(HeaderNames.ContentType, "x-custom-header");
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Para permitir todos los encabezados de solicitud de autor,llame a AllowAnyHeader :

using Microsoft.Net.Http.Headers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyAllowAllHeadersPolicy",
        builder =>
        {
            builder.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Los exploradores no son coherentes en la forma en que establecen Access-Control-Request-Headers . Si alguna de las dos:

  • Los encabezados se establecen en cualquier cosa que no sea "*"
  • AllowAnyHeader se llama a : incluya al menos , y , además de los encabezados personalizados Accept Content-Type que quiera Origin admitir.

Código de solicitud previo automático

Cuando se aplica la directiva cors:

  • Globalmente mediante una app.UseCors llamada a en Program.cs.
  • Uso del [EnableCors] atributo .

ASP.NET Core responde a la solicitud OPTIONS de la versión anterior.

La habilitación de CORS por punto de conexión con actualmente no admite solicitudes RequireCors automáticas previas.

En la sección Prueba de CORS de este documento se muestra este comportamiento.

Atributo [HttpOptions] para solicitudes previas

Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS. En algunos escenarios, puede que este no sea el caso. Por ejemplo, el uso de CORS con enrutamiento de punto de conexión.

El código siguiente usa el atributo [HttpOptions] para crear puntos de conexión para las solicitudes OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Consulte Test CORS with endpoint routing (Probar CORS con enrutamiento de puntos de conexión) y [HttpOptions] para obtener instrucciones sobre cómo probar el código anterior.

Establecer la hora de expiración previa

El encabezado especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud de Access-Control-Max-Age comprobación previa. Para establecer este encabezado, llame a SetPreflightMaxAge :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("MySetPreflightExpirationPolicy",
        builder =>
        {
            builder.WithOrigins("http://example.com")
                   .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Funcionamiento de CORS

En esta sección se describe lo que sucede en una solicitud CORS en el nivel de los mensajes HTTP.

  • CORS no es una característica de seguridad. CORS es un estándar W3C que permite que un servidor relaje la directiva del mismo origen.
    • Por ejemplo, un actor malintencionado podría usar scripting entre sitios (XSS) en el sitio y ejecutar una solicitud entre sitios a su sitio habilitado para CORS para robar información.
  • Una API no es más segura al permitir CORS.
    • Es el cliente (explorador) el que debe aplicar CORS. El servidor ejecuta la solicitud y devuelve la respuesta; es el cliente el que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
  • Es una manera de que un servidor permita que los exploradores ejecuten una solicitud XHR o Fetch API entre orígenes que, de lo contrario, estaría prohibida.
    • Los exploradores sin CORS no pueden realizar solicitudes entre orígenes. Antes de CORS, se usaba JSONP para sortear esta restricción. JSONP no usa XHR, usa la <script> etiqueta para recibir la respuesta. Los scripts se pueden cargar entre orígenes.

La especificación cors introdujo varios encabezados HTTP nuevos que habilitan las solicitudes entre orígenes. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. No se necesita código JavaScript personalizado para habilitar CORS.

Botón PUT test (Prueba PUT) en el ejemplo implementado

A continuación se muestra un ejemplo de una solicitud entre orígenes del botón De prueba De valores a https://cors1.azurewebsites.net/api/values . OriginEncabezado:

  • Proporciona el dominio del sitio que realiza la solicitud.
  • Es necesario y debe ser diferente del host.

Encabezados generales

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

Encabezados de respuesta

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

Encabezados de solicitud

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

En OPTIONS las solicitudes, el servidor establece el encabezado Encabezados de respuesta en la Access-Control-Allow-Origin: {allowed origin} respuesta. Por ejemplo, la solicitud de botón Delete [EnableCors] de ejemplo implementada OPTIONS contiene los siguientes encabezados:

Encabezados generales

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

Encabezados de respuesta

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

Encabezados de solicitud

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

En los encabezados de respuesta anteriores, el servidor establece el encabezado Access-Control-Allow-Origin en la respuesta. El https://cors1.azurewebsites.net valor de este encabezado coincide con el encabezado de la Origin solicitud.

Si se llama a , se devuelve el valor AllowAnyOrigin Access-Control-Allow-Origin: * comodín . AllowAnyOrigin permite cualquier origen.

Si la respuesta no incluye el encabezado , se produce un error en la solicitud Access-Control-Allow-Origin entre orígenes. En concreto, el explorador no permite la solicitud. Incluso si el servidor devuelve una respuesta correcta, el explorador no hace que la respuesta esté disponible para la aplicación cliente.

Visualización de solicitudes OPTIONS

De forma predeterminada, los exploradores Chrome y Edge no muestran solicitudes OPTIONS en la pestaña de red de las herramientas F12. Para mostrar las solicitudes OPTIONS en estos exploradores:

  • chrome://flags/#out-of-blink-cors o edge://flags/#out-of-blink-cors
  • deshabilite la marca.
  • Reiniciar.

Firefox muestra las solicitudes OPTIONS de forma predeterminada.

CORS en IIS

Al implementar en IIS, CORS debe ejecutarse antes Windows autenticación si el servidor no está configurado para permitir el acceso anónimo. Para admitir este escenario, el módulo CORS de IIS debe instalarse y configurarse para la aplicación.

Prueba de CORS

La descarga de ejemplo tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor Pages agregado:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

Advertencia

WithOrigins("https://localhost:<port>"); solo se debe usar para probar una aplicación de ejemplo similar al código de ejemplo de descarga.

A continuación ValuesController se proporcionan los puntos de conexión para las pruebas:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.

Pruebe el código de ejemplo anterior mediante uno de los enfoques siguientes:

  • Use la aplicación de ejemplo implementada en https://cors3.azurewebsites.net/ . No es necesario descargar el ejemplo.
  • Ejecute el ejemplo con dotnet run con la dirección URL predeterminada de https://localhost:5001 .
  • Ejecute el ejemplo desde Visual Studio con el puerto establecido en 44398 para una dirección URL de https://localhost:44398 .

Uso de un explorador con las herramientas de F12:

  • Seleccione el botón Valores y revise los encabezados en la pestaña Red.

  • Seleccione el botón PUT test (Prueba PUT). Consulte Visualización de solicitudes DE OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud OPTIONS. La prueba PUT crea dos solicitudes, una solicitud de configuración previa OPTIONS y la solicitud PUT.

  • Seleccione el GetValues2 [DisableCors] botón para desencadenar una solicitud CORS con error. Como se mencionó en el documento, la respuesta devuelve 200 correcto, pero no se realiza la solicitud cors. Seleccione la pestaña Consola para ver el error de CORS. En función del explorador, se muestra un error similar al siguiente:

    La directiva de CORS ha bloqueado el acceso para capturar desde el origen: no hay ningún encabezado 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' "Access-Control-Allow-Origin" en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.

Los puntos de conexión habilitados para CORS se pueden probar con una herramienta, como curl, Fiddlero Postman. Cuando se usa una herramienta, el origen de la solicitud especificada por el encabezado debe Origin diferir del host que recibe la solicitud. Si la solicitud no es entre orígenes en función del valor del Origin encabezado:

  • No es necesario que el middleware de CORS procese la solicitud.
  • Los encabezados CORS no se devuelven en la respuesta.

El comando siguiente usa curl para emitir una solicitud OPTIONS con información:

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Prueba de CORS con enrutamiento de puntos de conexión y [HttpOptions]

La habilitación de CORS por punto de conexión con actualmente no admite solicitudes de comprobación RequireCors previa automáticas. Tenga en cuenta el código siguiente, que usa el enrutamiento de puntos de conexión para habilitar CORS:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
});

builder.Services.AddControllers();
builder.Services.AddRazorPages();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();
app.MapRazorPages();

app.Run();

A continuación TodoItems1Controller se proporcionan puntos de conexión para las pruebas:

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Pruebe el código anterior desde la página de prueba del ejemplo implementado.

Los botones Eliminar [EnableCors] y GET [EnableCors] se han hecho correctamente, ya que los puntos de conexión tienen y responden a solicitudes [EnableCors] previas. Se produce un error en los demás puntos de conexión. Se produce un error en el botón GET, porque JavaScript envía:

 headers: {
      "Content-Type": "x-custom-header"
 },

A continuación TodoItems2Controller se proporcionan puntos de conexión similares, pero incluye código explícito para responder a las solicitudes OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    [EnableCors]  // Rquired for this path
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]  // Rquired for this path
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Pruebe el código anterior desde la página de prueba del ejemplo implementado. En la lista desplegable Controlador, seleccione Preflight y, a continuación, Set Controller (Establecer controlador). Todas las llamadas de CORS a los TodoItems2Controller puntos de conexión se realiza correctamente.

Recursos adicionales

Por Rick Anderson y Kirk Larkin

En este artículo se muestra cómo habilitar CORS en una ASP.NET Core aplicación.

La seguridad del explorador evita que una página web realice solicitudes a un dominio diferente del que atendió a dicha página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen evita que un sitio malintencionado lea información confidencial de otro sitio. A veces, es posible que quiera permitir que otros sitios realicen solicitudes entre orígenes a la aplicación. Para obtener más información, consulte el artículo de Mozilla CORS.

Uso compartido de recursos entre orígenes (CORS):

  • Es un estándar W3C que permite que un servidor relaje la directiva del mismo origen.
  • No es una característica de seguridad, CORS relaja la seguridad. Una API no es más segura al permitir CORS. Para obtener más información, vea Funcionamiento de CORS.
  • Permite que un servidor permita explícitamente algunas solicitudes entre orígenes mientras rechaza otras.
  • Es más seguro y flexible que las técnicas anteriores, como JSONP.

Vea o descargue el código de ejemplo (cómo descargarlo)

Mismo origen

Dos direcciones URL tienen el mismo origen si tienen esquemas, hosts y puertos idénticos(RFC 6454).

Estas dos direcciones URL tienen el mismo origen:

  • https://example.com/foo.html
  • https://example.com/bar.html

Estas direcciones URL tienen orígenes diferentes a los de las dos direcciones URL anteriores:

  • https://example.net: dominio diferente
  • https://www.example.com/foo.html: subdominio diferente
  • http://example.com/foo.html: esquema diferente
  • https://example.com:9000/foo.html: puerto diferente

Habilitación de CORS

Hay tres maneras de habilitar CORS:

El uso del atributo [EnableCors] con una directiva con nombre proporciona el control más preciso para limitar los puntos de conexión que admiten CORS.

Advertencia

UseCors debe llamarse en el orden correcto. Para obtener más información, vea Orden de middleware. Por ejemplo, UseCors se debe llamar a antes de cuando se usa UseResponseCaching UseResponseCaching .

Cada enfoque se detalla en las secciones siguientes.

CORS con directiva con nombre y middleware

El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva CORS a todos los puntos de conexión de la aplicación con los orígenes especificados:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

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

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

El código anterior:

  • Establece el nombre de la directiva en _myAllowSpecificOrigins . El nombre de la directiva es arbitrario.
  • Llama al UseCors método de extensión y especifica la directiva _myAllowSpecificOrigins CORS. UseCors agrega el middleware de CORS. La llamada a UseCors debe colocarse después de UseRouting , pero antes de UseAuthorization . Para obtener más información, vea Orden de middleware.
  • Llama AddCors a con una expresión lambda. La expresión lambda toma un CorsPolicyBuilder objeto . Las opciones deconfiguración, como WithOrigins , se describen más adelante en este artículo.
  • Habilita la _myAllowSpecificOrigins directiva CORS para todos los puntos de conexión del controlador. Consulte Enrutamiento de puntos de conexión para aplicar una directiva de CORS a puntos de conexión específicos.
  • Cuando se usa middleware de almacenamiento en caché de respuestas,llame a antes de UseCors UseResponseCaching .

Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting y UseEndpoints .

Consulte Test CORS (Probar CORS) para obtener instrucciones sobre cómo probar código similar al código anterior.

La AddCors llamada al método agrega servicios CORS al contenedor de servicios de la aplicación:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

Para más información, consulte Opciones de directiva de CORS en este documento.

Los CorsPolicyBuilder métodos se pueden encadenar, como se muestra en el código siguiente:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
    });

    services.AddControllers();
}

Nota: La dirección URL especificada no debe contener una barra diagonal final ( / ). Si la dirección URL finaliza con / , la comparación devuelve y no se devuelve ningún false encabezado.

CORS con directiva predeterminada y middleware

El código resaltado siguiente habilita la directiva CORS predeterminada:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });

        services.AddControllers();
    }

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

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

El código anterior aplica la directiva CORS predeterminada a todos los puntos de conexión del controlador.

Habilitación de CORS con enrutamiento de punto de conexión

La habilitación de CORS por punto de conexión mediante no admite solicitudes de comprobación RequireCors previa automáticas. Para obtener más información, vea este GitHub y Prueba de CORS con enrutamiento de puntos de conexión y [HttpOptions].

Con el enrutamiento de puntos de conexión, CORS se puede habilitar por punto de conexión mediante el RequireCors conjunto de métodos de extensión:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

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

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/echo",
                context => context.Response.WriteAsync("echo"))
                .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapControllers()
                     .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapGet("/echo2",
                context => context.Response.WriteAsync("echo2"));

            endpoints.MapRazorPages();
        });
    }
}

En el código anterior:

  • app.UseCors habilita el middleware de CORS. Dado que no se ha configurado una directiva predeterminada, app.UseCors() por sí sola no habilita CORS.
  • Los /echo puntos de conexión de controlador y permiten solicitudes entre orígenes mediante la directiva especificada.
  • Los /echo2 puntos de conexión y Pages no Razor permiten solicitudes entre orígenes porque no se especificó ninguna directiva predeterminada.

El atributo [DisableCors] no deshabilita CORS habilitado por el enrutamiento de puntos de conexión con RequireCors .

Consulte Test CORS with endpoint routing (Probar CORS con enrutamiento de puntos de conexión) y [HttpOptions] para obtener instrucciones sobre cómo probar código similar al anterior.

Habilitación de CORS con atributos

Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporciona el control más preciso.

El atributo [EnableCors] proporciona una alternativa a la aplicación global de CORS. El [EnableCors] atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:

  • [EnableCors] especifica la directiva predeterminada.
  • [EnableCors("{Policy String}")] especifica una directiva con nombre.

El [EnableCors] atributo se puede aplicar a:

  • Razor Página PageModel
  • Controller
  • Método de acción del controlador

Se pueden aplicar directivas diferentes a controladores, modelos de página o métodos de acción con el [EnableCors] atributo . Cuando el atributo se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican [EnableCors] ambas directivas. Se recomienda no combinar directivas. Use el [EnableCors] atributo o middleware, no ambos en la misma aplicación.

El código siguiente aplica una directiva diferente a cada método:

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

El código siguiente crea dos directivas CORS:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("Policy1",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });
        });

        services.AddControllers();
    }

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

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Para el control más preciso de la limitación de las solicitudes de CORS:

  • Use [EnableCors("MyPolicy")] con una directiva con nombre.
  • No defina una directiva predeterminada.
  • No use el enrutamiento de puntos de conexión.

El código de la sección siguiente cumple la lista anterior.

Consulte Test CORS (Probar CORS) para obtener instrucciones sobre cómo probar código similar al código anterior.

Deshabilitar CORS

El atributo [DisableCors] no deshabilita CORS habilitado por el enrutamiento de puntos de conexión.

El código siguiente define la directiva cors "MyPolicy" :

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

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

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

El código siguiente deshabilita CORS para la GetValues2 acción:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

El código anterior:

Consulte Test CORS (Probar CORS) para obtener instrucciones sobre cómo probar el código anterior.

Opciones de directiva de CORS

En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:

AddPolicy se llama en Startup.ConfigureServices . Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS.

Establecer los orígenes permitidos

AllowAnyOrigin: permite solicitudes CORS desde todos los orígenes con cualquier esquema ( http o https ). AllowAnyOrigin no es seguro porque cualquier sitio web puede realizar solicitudes entre orígenes a la aplicación.

Nota

Especificar AllowAnyOrigin y AllowCredentials es una configuración no segura y puede dar lugar a una falsificación de solicitud entre sitios. El servicio CORS devuelve una respuesta CORS no válida cuando una aplicación está configurada con ambos métodos.

AllowAnyOrigin afecta a las solicitudes de nivel previo y al Access-Control-Allow-Origin encabezado. Para obtener más información, consulte la sección Solicitudes de comprobación previa.

SetIsOriginAllowedToAllowWildcardSubdomains: establece la propiedad de la directiva para que sea una función que permita que los orígenes coincidan con un dominio con caracteres comodín configurado al evaluar si se permite IsOriginAllowed el origen.

options.AddPolicy("MyAllowSubdomainPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
            .SetIsOriginAllowedToAllowWildcardSubdomains();
    });

Establecer los métodos HTTP permitidos

AllowAnyMethod:

  • Permite cualquier método HTTP:
  • Afecta a las solicitudes de nivel previo y al Access-Control-Allow-Methods encabezado. Para obtener más información, consulte la sección Solicitudes de comprobación previa.

Establecimiento de los encabezados de solicitud permitidos

Para permitir que se envíen encabezados específicos en una solicitud CORS, denominadosencabezados de solicitud de autor, llame a WithHeaders y especifique los encabezados permitidos:

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Para permitir todos los encabezados de solicitud de autor,llame a AllowAnyHeader :

options.AddPolicy("MyAllowAllHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

AllowAnyHeaderafecta a las solicitudes de nivel previo y al encabezado Access-Control-Request-Headers. Para obtener más información, consulte la sección Solicitudes de comprobación previa.

Una coincidencia de directiva de middleware de CORS con encabezados específicos especificados por solo es posible cuando los encabezados enviados en coinciden exactamente con los WithHeaders Access-Control-Request-Headers encabezados indicados en WithHeaders .

Por ejemplo, considere una aplicación configurada de la siguiente manera:

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

El middleware de CORS rechaza una solicitud de nivel previo con el siguiente encabezado de solicitud porque Content-Language (HeaderNames.ContentLanguage) no aparece en WithHeaders :

Access-Control-Request-Headers: Cache-Control, Content-Language

La aplicación devuelve una respuesta 200 OK, pero no devuelve los encabezados CORS. Por lo tanto, el explorador no intenta realizar la solicitud entre orígenes.

Establecer los encabezados de respuesta expuestos

De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, vea W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Los encabezados de respuesta que están disponibles de forma predeterminada son:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

La especificación cors llama a estos encabezados encabezados de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a WithExposedHeaders :

options.AddPolicy("MyExposeResponseHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .WithExposedHeaders("x-custom-header");
    });

Credenciales en solicitudes entre orígenes

Las credenciales requieren un control especial en una solicitud cors. De forma predeterminada, el explorador no envía credenciales con una solicitud entre orígenes. Las credenciales incluyen cookie s y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud entre orígenes, el cliente debe establecer XMLHttpRequest.withCredentials en true .

Usar XMLHttpRequest directamente:

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Uso de jQuery:

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Mediante fetch API:

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

El servidor debe permitir las credenciales. Para permitir credenciales entre orígenes, llame a AllowCredentials :

options.AddPolicy("MyMyAllowCredentialsPolicy",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowCredentials();
    });

La respuesta HTTP incluye un encabezado , que indica al explorador que el servidor permite Access-Control-Allow-Credentials credenciales para una solicitud entre orígenes.

Si el explorador envía credenciales pero la respuesta no incluye un encabezado válido, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud Access-Control-Allow-Credentials entre orígenes.

Permitir credenciales entre orígenes es un riesgo de seguridad. Un sitio web de otro dominio puede enviar las credenciales de un usuario que ha iniciado sesión a la aplicación en nombre del usuario sin que el usuario lo sepa.

La especificación de CORS también indica que establecer los orígenes en (todos los orígenes) no es válido "*" si el encabezado está Access-Control-Allow-Credentials presente.

Solicitudes de comprobación previa

Para algunas solicitudes CORS, el explorador envía una solicitud OPTIONS adicional antes de realizar la solicitud real. Esta solicitud se denomina solicitud de comprobación previa. El explorador puede omitir la solicitud de comprobación previa si se cumplen todas las condiciones siguientes:

  • El método de solicitud es GET, HEAD o POST.
  • La aplicación no establece encabezados de solicitud distintos de Accept , , , o Accept-Language Content-Language Content-Type Last-Event-ID .
  • El Content-Type encabezado, si se establece, tiene uno de los valores siguientes:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

La regla sobre los encabezados de solicitud establecidos para la solicitud de cliente se aplica a los encabezados que establece la aplicación mediante una setRequestHeader llamada a en el objeto XMLHttpRequest . La especificación de CORS llama a estos encabezados para crear encabezados de solicitud. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent Host , o Content-Length .

A continuación se muestra una respuesta de ejemplo similar a la solicitud previa realizada desde el botón [Put test] de la sección Test CORS (Probar CORS) de este documento.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

La solicitud de comprobación previa usa el método HTTP OPTIONS. Puede incluir los siguientes encabezados:

Si se deniega la solicitud previa, la aplicación devuelve una 200 OK respuesta, pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta realizar la solicitud entre orígenes. Para obtener un ejemplo de una solicitud previa denegada, consulte la sección Prueba de CORS de este documento.

Con las herramientas de F12, la aplicación de consola muestra un error similar a uno de los siguientes, dependiendo del explorador:

  • Firefox: solicitud entre orígenes bloqueada: la misma directiva de origen no permite leer el recurso remoto en https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 . (Motivo: la solicitud de CORS no se ha hecho correctamente). Más información
  • Chromium basado en: la directiva CORS ha bloqueado el acceso para capturar en "" desde el origen": La respuesta a la solicitud de comprobación de control de acceso no pasa la comprobación de control de acceso: no hay ningún encabezado https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net "Access-Control-Allow-Origin" en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.

Para permitir encabezados específicos, llame a WithHeaders :

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Para permitir todos los encabezados de solicitud de autor,llame a AllowAnyHeader :

options.AddPolicy("MyAllowAllHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

Los exploradores no son coherentes en la forma en que establecen Access-Control-Request-Headers . Si alguna de las dos:

  • Los encabezados se establecen en cualquier cosa que no sea "*"
  • AllowAnyHeader se llama a : incluya al menos , y , además de los encabezados personalizados Accept Content-Type que quiera Origin admitir.

Código de solicitud previo automático

Cuando se aplica la directiva cors:

  • Globalmente mediante una llamada app.UseCors a en Startup.Configure .
  • Uso del [EnableCors] atributo .

ASP.NET Core responde a la solicitud OPTIONS.

La habilitación de CORS por punto de conexión con actualmente no admite solicitudes RequireCors automáticas previas.

En la sección Test CORS (Cors de prueba) de este documento se muestra este comportamiento.

Atributo [HttpOptions] para solicitudes previas

Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS. En algunos escenarios, puede que este no sea el caso. Por ejemplo, el uso de CORS con enrutamiento de punto de conexión.

El código siguiente usa el atributo [HttpOptions] para crear puntos de conexión para las solicitudes OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Consulte Test CORS with endpoint routing (Probar CORS con enrutamiento de puntos de conexión) y [HttpOptions] para obtener instrucciones sobre cómo probar el código anterior.

Establecer la hora de expiración previa

El encabezado especifica cuánto tiempo se puede almacenar en caché la respuesta a la Access-Control-Max-Age solicitud anterior. Para establecer este encabezado, llame a SetPreflightMaxAge :

options.AddPolicy("MySetPreflightExpirationPolicy",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

Funcionamiento de CORS

En esta sección se describe lo que sucede en una solicitud CORS en el nivel de los mensajes HTTP.

  • CORS no es una característica de seguridad. CORS es un estándar W3C que permite a un servidor relajar la directiva del mismo origen.
    • Por ejemplo, un actor malintencionado podría usar scripting entre sitios (XSS) en el sitio y ejecutar una solicitud entre sitios a su sitio habilitado para CORS para robar información.
  • Una API no es más segura al permitir CORS.
    • Es el cliente (explorador) el que debe aplicar CORS. El servidor ejecuta la solicitud y devuelve la respuesta; es el cliente el que devuelve un error y bloquea la respuesta. Por ejemplo, cualquiera de las siguientes herramientas mostrará la respuesta del servidor:
  • Es una manera de que un servidor permita que los exploradores ejecuten una solicitud XHR o Fetch API entre orígenes que, de lo contrario, se prohibiría.
    • Los exploradores sin CORS no pueden realizar solicitudes entre orígenes. Antes de CORS, se usaba JSONP para eludir esta restricción. JSONP no usa XHR, usa la <script> etiqueta para recibir la respuesta. Los scripts se pueden cargar entre orígenes.

La especificación de CORS introdujo varios encabezados HTTP nuevos que habilitan las solicitudes entre orígenes. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. No se requiere código JavaScript personalizado para habilitar CORS.

Botón PUT test (Prueba PUT) en el ejemplo implementado

A continuación se muestra un ejemplo de una solicitud entre orígenes desde el botón Prueba de valores a https://cors1.azurewebsites.net/api/values . OriginEncabezado:

  • Proporciona el dominio del sitio que realiza la solicitud.
  • Es necesario y debe ser diferente del host.

Encabezados generales

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

Encabezados de respuesta

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

Encabezados de solicitud

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

En OPTIONS las solicitudes, el servidor establece el encabezado Encabezados de respuesta en la Access-Control-Allow-Origin: {allowed origin} respuesta. Por ejemplo, la solicitud de botónDelete [EnableCors] de ejemplo implementada OPTIONS contiene los encabezados siguientes:

Encabezados generales

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

Encabezados de respuesta

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

Encabezados de solicitud

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

En los encabezados de respuesta anteriores, el servidor establece el encabezado Access-Control-Allow-Origin en la respuesta. El https://cors1.azurewebsites.net valor de este encabezado coincide con el encabezado de la Origin solicitud.

Si se llama a , se devuelve , el AllowAnyOrigin Access-Control-Allow-Origin: * valor comodín. AllowAnyOrigin permite cualquier origen.

Si la respuesta no incluye el encabezado , se produce un error en la Access-Control-Allow-Origin solicitud entre orígenes. En concreto, el explorador no permite la solicitud. Incluso si el servidor devuelve una respuesta correcta, el explorador no hace que la respuesta esté disponible para la aplicación cliente.

Visualización de solicitudes OPTIONS

De forma predeterminada, los exploradores Chrome y Edge no muestran las solicitudes OPTIONS en la pestaña de red de las herramientas de F12. Para mostrar las solicitudes OPTIONS en estos exploradores:

  • chrome://flags/#out-of-blink-cors o edge://flags/#out-of-blink-cors
  • deshabilite la marca.
  • Reiniciar.

Firefox muestra las solicitudes OPTIONS de forma predeterminada.

CORS en IIS

Al implementar en IIS, CORS debe ejecutarse antes de Windows autenticación si el servidor no está configurado para permitir el acceso anónimo. Para admitir este escenario, el módulo CORS de IIS debe instalarse y configurarse para la aplicación.

Prueba de CORS

La descarga de ejemplo tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor Pages agregado:

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Advertencia

WithOrigins("https://localhost:<port>"); solo se debe usar para probar una aplicación de ejemplo similar al código de ejemplo de descarga.

A continuación ValuesController se proporcionan los puntos de conexión para las pruebas:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

El paquete de NuGet Rick.Docs.Samples.RouteInfo proporciona MyDisplayRouteInfo y se muestra la información de ruta.

Pruebe el código de ejemplo anterior mediante uno de los enfoques siguientes:

  • Use la aplicación de ejemplo implementada en https://cors3.azurewebsites.net/ . No es necesario descargar el ejemplo.
  • Ejecute el ejemplo con dotnet run con la dirección URL predeterminada de https://localhost:5001 .
  • Ejecute el ejemplo desde Visual Studio con el puerto establecido en 44398 para una dirección URL de https://localhost:44398 .

Uso de un explorador con las herramientas de F12:

  • Seleccione el botón Valores y revise los encabezados en la pestaña Red.

  • Seleccione el botón PUT test (Prueba PUT). Consulte Visualización de solicitudes DE OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud OPTIONS. La prueba PUT crea dos solicitudes, una solicitud de configuración options y la solicitud PUT.

  • Seleccione el GetValues2 [DisableCors] botón para desencadenar una solicitud DE CORS con error. Como se mencionó en el documento, la respuesta devuelve 200 correcto, pero no se realiza la solicitud cors. Seleccione la pestaña Consola para ver el error de CORS. En función del explorador, se muestra un error similar al siguiente:

    La directiva de CORS ha bloqueado el acceso para capturar desde el origen: no hay ningún encabezado 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' "Access-Control-Allow-Origin" en el recurso solicitado. Si una respuesta opaca sirve a sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.

Los puntos de conexión habilitados para CORS se pueden probar con una herramienta, como curl, Fiddlero Postman. Cuando se usa una herramienta, el origen de la solicitud especificada por el encabezado Origin debe diferir del host que recibe la solicitud. Si la solicitud no es entre orígenes en función del valor del Origin encabezado:

  • No es necesario que el middleware de CORS procese la solicitud.
  • Los encabezados CORS no se devuelven en la respuesta.

El siguiente comando usa curl para emitir una solicitud OPTIONS con información:

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Prueba de CORS con enrutamiento de puntos de conexión y [HttpOptions]

La habilitación de CORS por punto de conexión con actualmente no admite solicitudes de actualización RequireCors automáticas . Tenga en cuenta el código siguiente que usa el enrutamiento de puntos de conexión para habilitar CORS:

public class StartupEndPointBugTest
{
    readonly string MyPolicy = "_myPolicy";

    // .WithHeaders(HeaderNames.ContentType, "x-custom-header")
    // forces browsers to require a preflight request with GET

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyPolicy,
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com",
                                        "https://cors1.azurewebsites.net",
                                        "https://cors3.azurewebsites.net",
                                        "https://localhost:44398",
                                        "https://localhost:5001")
                           .WithHeaders(HeaderNames.ContentType, "x-custom-header")
                           .WithMethods("PUT", "DELETE", "GET", "OPTIONS");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireCors(MyPolicy);
            endpoints.MapRazorPages();
        });
    }
}

A continuación TodoItems1Controller se proporcionan puntos de conexión para las pruebas:

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Pruebe el código anterior desde la página de prueba del ejemplo implementado.

Los botones Eliminar [EnableCors] y GET [EnableCors] se han hecho correctamente, ya que los puntos de conexión tienen y responden a solicitudes [EnableCors] previas. Se produce un error en los demás puntos de conexión. Se produce un error en el botón GET, porque JavaScript envía:

 headers: {
      "Content-Type": "x-custom-header"
 },

A continuación TodoItems2Controller se proporcionan puntos de conexión similares, pero incluye código explícito para responder a las solicitudes OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    [EnableCors]  // Rquired for this path
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]  // Rquired for this path
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Pruebe el código anterior desde la página de prueba del ejemplo implementado. En la lista desplegable Controlador, seleccione Preflight y, a continuación, Establecer controlador. Todas las llamadas de CORS a los TodoItems2Controller puntos de conexión se realiza correctamente.

Recursos adicionales