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.htmlhttps://example.com/bar.html
Estas direcciones URL tienen orígenes diferentes a los de las dos direcciones URL anteriores:
https://example.net: dominio diferentehttps://www.example.com/foo.html: subdominio diferentehttp://example.com/foo.html: esquema diferentehttps://example.com:9000/foo.html: puerto diferente
Habilitación de CORS
Hay tres maneras de habilitar CORS:
- En middleware mediante una directiva con nombre o una directiva predeterminada.
- Mediante el enrutamiento de puntos de conexión.
- Con el atributo [EnableCors].
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
_myAllowSpecificOriginsCORS.UseCorsagrega el middleware cors. La llamada aUseCorsdebe colocarse después deUseRouting, pero antes deUseAuthorization. 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
_myAllowSpecificOriginsdirectiva 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.UseCorshabilita el middleware de CORS. Dado que no se ha configurado una directiva predeterminada,app.UseCors()por sí sola no habilita CORS.- Los
/echopuntos de conexión de controlador y permiten solicitudes entre orígenes mediante la directiva especificada. - Los
/echo2puntos 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:
- No habilita CORS con enrutamiento de punto de conexión.
- No define una directiva de CORS predeterminada.
- Usa [EnableCors("MyPolicy")] para habilitar la
"MyPolicy"directiva CORS para el controlador. - Deshabilita CORS para el
GetValues2método .
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:
- Establecer los orígenes permitidos
- Establecer los métodos HTTP permitidos
- Establecimiento de los encabezados de solicitud permitidos
- Establecer los encabezados de respuesta expuestos
- Credenciales en solicitudes entre orígenes
- Establecer la hora de expiración previa
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
- Permite cualquier método HTTP:
- Afecta a las solicitudes de nivel previo y al
Access-Control-Allow-Methodsencabezado. 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-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma
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, , , oAccept-LanguageContent-LanguageContent-TypeLast-Event-ID. - El
Content-Typeencabezado, si se establece, tiene uno de los valores siguientes:application/x-www-form-urlencodedmultipart/form-datatext/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:
- Access-Control-Request-Method:método HTTP que se usará para la solicitud real.
- Access-Control-Request-Headers:una lista de encabezados de solicitud que la aplicación establece en la solicitud real. Como se indicó anteriormente, esto no incluye los encabezados que establece el explorador, como
User-Agent. - Métodos access-control-allow-methods
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
AcceptContent-Typeque quieraOriginadmitir.
Código de solicitud previo automático
Cuando se aplica la directiva cors:
- Globalmente mediante una
app.UseCorsllamada 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:
- Fiddler
- Postman
- HttpClient de .NET
- Un explorador web especificando la dirección URL en la barra de direcciones.
- 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.
- 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
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-corsoedge://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 runcon la dirección URL predeterminada dehttps://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.htmlhttps://example.com/bar.html
Estas direcciones URL tienen orígenes diferentes a los de las dos direcciones URL anteriores:
https://example.net: dominio diferentehttps://www.example.com/foo.html: subdominio diferentehttp://example.com/foo.html: esquema diferentehttps://example.com:9000/foo.html: puerto diferente
Habilitación de CORS
Hay tres maneras de habilitar CORS:
- En middleware mediante una directiva con nombre o una directiva predeterminada.
- Uso del enrutamiento de puntos de conexión.
- Con el atributo [EnableCors].
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
_myAllowSpecificOriginsCORS.UseCorsagrega el middleware de CORS. La llamada aUseCorsdebe colocarse después deUseRouting, pero antes deUseAuthorization. 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
_myAllowSpecificOriginsdirectiva 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.UseCorshabilita el middleware de CORS. Dado que no se ha configurado una directiva predeterminada,app.UseCors()por sí sola no habilita CORS.- Los
/echopuntos de conexión de controlador y permiten solicitudes entre orígenes mediante la directiva especificada. - Los
/echo2puntos 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:
- No habilita CORS con enrutamiento de punto de conexión.
- No define una directiva de CORS predeterminada.
- Usa [EnableCors("MyPolicy")] para habilitar la
"MyPolicy"directiva CORS para el controlador. - Deshabilita CORS para el
GetValues2método .
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:
- Establecer los orígenes permitidos
- Establecer los métodos HTTP permitidos
- Establecimiento de los encabezados de solicitud permitidos
- Establecer los encabezados de respuesta expuestos
- Credenciales en solicitudes entre orígenes
- Establecer la hora de expiración previa
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
- Permite cualquier método HTTP:
- Afecta a las solicitudes de nivel previo y al
Access-Control-Allow-Methodsencabezado. 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-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma
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, , , oAccept-LanguageContent-LanguageContent-TypeLast-Event-ID. - El
Content-Typeencabezado, si se establece, tiene uno de los valores siguientes:application/x-www-form-urlencodedmultipart/form-datatext/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:
- Access-Control-Request-Method:el método HTTP que se usará para la solicitud real.
- Access-Control-Request-Headers:una lista de encabezados de solicitud que la aplicación establece en la solicitud real. Como se indicó anteriormente, esto no incluye los encabezados que establece el explorador, como
User-Agent. - Access-Control-Allow-Methods
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
AcceptContent-Typeque quieraOriginadmitir.
Código de solicitud previo automático
Cuando se aplica la directiva cors:
- Globalmente mediante una llamada
app.UseCorsa enStartup.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:
- Fiddler
- Postman
- .NET HttpClient
- Un explorador web especificando la dirección URL en la barra de direcciones.
- 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.
- 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
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-corsoedge://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 runcon la dirección URL predeterminada dehttps://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.