Evento
Campionato do Mundo de Power BI DataViz
Feb 14, 4 PM - Mar 31, 4 PM
Con 4 posibilidades de entrar, poderías gañar un paquete de conferencias e facelo ao Live Grand Finale en Las Vegas
Máis informaciónEste explorador xa non é compatible.
Actualice a Microsoft Edge para dispoñer das funcionalidades máis recentes, as actualizacións de seguranza e a asistencia técnica.
Nota
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Aviso
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo Cross-Origin Resource Sharing (CORS o Uso compartido de recursos entre orígenes) se habilita en una aplicación ASP.NET Core.
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 CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
Vea o descargue el código de ejemplo (cómo descargarlo)
Dos 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 las dos direcciones URL anteriores:
https://example.net
: Dominio diferentehttps://contoso.example.com/foo.html
: Subdominio diferentehttp://example.com/foo.html
: Esquema diferentehttps://example.com:9000/foo.html
: Puerto diferenteExisten tres formas de habilitar CORS:
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Aviso
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de 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,
policy =>
{
policy.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:
_myAllowSpecificOrigins
. El nombre de la política es arbitrario._myAllowSpecificOrigins
directiva 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.WithOrigins
, se describen más adelante en este artículo._myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos.Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
La AddCors llamada al método añade 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,
policy =>
{
policy.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 obtener más información, consulte Opciones de directiva 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,
policy =>
{
policy.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 URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
Normalmente, UseStaticFiles
se llama a antes de UseCors
. Las aplicaciones que usan JavaScript para recuperar archivos estáticos entre sitios deben llamar a UseCors
antes de UseStaticFiles
.
El código resaltado siguiente habilita la directiva CORS predeterminada:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.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.
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando 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,
policy =>
{
policy.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
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada, app.UseCors()
por sí solo no habilita CORS./echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada./echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.El [DisableCors]atributo no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
Consulte Probar CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar código similar al anterior.
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.El atributo [EnableCors]
se puede aplicar a:
PageModel
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, 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 siguiente código crea dos políticas CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.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 obtener el mejor control de la limitación de solicitudes CORS:
[EnableCors("MyPolicy")]
con una directiva con nombre.El código de la sección siguiente cumple la lista anterior.
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.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:
"MyPolicy"
directiva CORS para el controlador.GetValues2
método .Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
AddPolicy es llamado por Program.cs
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
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 preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de 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,
policy =>
{
policy.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, llama a AllowAnyHeader:
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria 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 la solicitud entre orígenes.
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte 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 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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
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
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a AllowCredentials:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud 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 el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
Accept
, Accept-Language
, Content-Language
, Content-Type
o Last-Event-ID
.Content-Type
, si está configurado, tiene uno de los siguientes valores: application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
Nota
En este artículo se incluyen las direcciones URL creadas al implementar el código de ejemplo en dos sitios web de Azure, https://cors3.azurewebsites.net
y https://cors.azurewebsites.net
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test 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 preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
User-Agent
.Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más informaciónPara permitir encabezados específicos, llame a WithHeaders:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.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, llama a AllowAnyHeader:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
"*"
Accept
, Content-Type
y Origin
, además de los encabezados personalizados que quiera admitir.Cuando se aplica la directiva CORS:
app.UseCors
en Program.cs
.[EnableCors]
.ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
La sección CORS de prueba de este documento muestra este comportamiento.
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS.
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 Prueba CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar el código anterior.
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a SetPreflightMaxAge:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
El siguiente es un ejemplo de una solicitud de origen cruzado desde el botón de prueba Valores a https://cors1.azurewebsites.net/api/values
. El encabezado Origin
:
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 las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, en el código de ejemplo, la solicitud OPTIONS
del botón Delete [EnableCors]
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 valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador 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.
Las solicitudes a un punto de conexión usando HTTP que son redirigidas a HTTPS por UseHttpsRedirection fallan con ERR_INVALID_REDIRECT on the CORS preflight request
.
Los proyectos de API pueden rechazar solicitudes HTTP en lugar de usar UseHttpsRedirection
para redirigir solicitudes a HTTPS.
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows 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.
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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();
Aviso
WithOrigins("https://localhost:<port>");
solo se debería 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 métodos siguientes:
dotnet run
utilizando la URL predeterminada de https://localhost:5001
.https://localhost:44398
.Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES 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. Dependiendo del navegador, se muestra un error similar al siguiente:
El acceso a la obtención en 'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen 'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente 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 finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza 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 de origen cruzado según el valor del encabezado Origin
:
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Considere el siguiente código que utiliza enrutamiento de punto final para habilitar CORS por punto final utilizando RequireCors
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Observe que solo el /echo
punto de conexión está utilizando el RequireCors
para permitir solicitudes de origen cruzado utilizando la directiva especificada. Los controladores siguientes habilitan CORS mediante el atributo [EnableCors].
A continuación TodoItems1Controller
se proporcionan los 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("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se 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] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
El código anterior se puede probar mediante la implementación del ejemplo en Azure. En la lista desplegable Controlador, seleccione Preflight y, a continuación, Establecer controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo habilitar CORS en una aplicación de ASP.NET Core.
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 CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
Vea o descargue el código de ejemplo (cómo descargarlo)
Dos 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 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 diferenteExisten tres formas de habilitar CORS:
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Aviso
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de 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,
policy =>
{
policy.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:
_myAllowSpecificOrigins
. El nombre de la política es arbitrario._myAllowSpecificOrigins
directiva 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.WithOrigins
, se describen más adelante en este artículo._myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos.Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
La AddCors llamada al método añade 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,
policy =>
{
policy.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 obtener más información, consulte Opciones de directiva 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,
policy =>
{
policy.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 URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
Aviso
UseCors
debe colocarse después UseRouting
y antes de UseAuthorization
. Esto es para asegurarse de que los encabezados CORS se incluyen en la respuesta para las llamadas autorizadas y no autorizadas.
Normalmente, UseStaticFiles
se llama a antes de UseCors
. Las aplicaciones que usan JavaScript para recuperar archivos estáticos entre sitios deben llamar a UseCors
antes de UseStaticFiles
.
El código resaltado siguiente habilita la directiva CORS predeterminada:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.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.
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando 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,
policy =>
{
policy.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
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada, app.UseCors()
por sí solo no habilita CORS./echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada./echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.El [DisableCors]atributo no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
En ASP.NET Core 7.0, el [EnableCors]
atributo debe pasar un parámetro o una advertencia ASP0023 a partir de una coincidencia ambigua en la ruta. ASP.NET Core 8.0 y versiones posteriores no genera la ASP0023
advertencia.
[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] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Consulte Probar CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar código similar al anterior.
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.El atributo [EnableCors]
se puede aplicar a:
PageModel
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, 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 siguiente código crea dos políticas CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.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 obtener el mejor control de la limitación de solicitudes CORS:
[EnableCors("MyPolicy")]
con una directiva con nombre.El código de la sección siguiente cumple la lista anterior.
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.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:
"MyPolicy"
directiva CORS para el controlador.GetValues2
método .Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
AddPolicy es llamado por Program.cs
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
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 preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de 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,
policy =>
{
policy.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,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria 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 la solicitud entre orígenes.
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte 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 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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
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
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a AllowCredentials:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud 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 el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
Accept
, Accept-Language
, Content-Language
, Content-Type
o Last-Event-ID
.Content-Type
, si está configurado, tiene uno de los siguientes valores: application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test 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 preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
User-Agent
.Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más informaciónPara permitir encabezados específicos, llame a WithHeaders:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
"*"
Accept
, Content-Type
y Origin
, además de los encabezados personalizados que quiera admitir.Cuando se aplica la directiva CORS:
app.UseCors
en Program.cs
.[EnableCors]
.ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
La sección CORS de prueba de este documento muestra este comportamiento.
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS.
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 Prueba CORS con el atributo [EnableCors] y el método RequireCors para obtener instrucciones sobre cómo probar el código anterior.
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a SetPreflightMaxAge:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
Selecciona el botón de prueba PUT de la muestra implementada.
El encabezado Origin
:
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 las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, en el código de ejemplo, la solicitud OPTIONS
del botón Delete [EnableCors]
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 valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador 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.
Las solicitudes a un punto de conexión usando HTTP que son redirigidas a HTTPS por UseHttpsRedirection fallan con ERR_INVALID_REDIRECT on the CORS preflight request
.
Los proyectos de API pueden rechazar solicitudes HTTP en lugar de usar UseHttpsRedirection
para redirigir solicitudes a HTTPS.
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows 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.
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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();
Aviso
WithOrigins("https://localhost:<port>");
solo se debería 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 métodos siguientes:
dotnet run
utilizando la URL predeterminada de https://localhost:5001
.https://localhost:44398
.Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES 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. Dependiendo del navegador, se muestra un error similar al siguiente:
El acceso a la obtención en 'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen 'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente 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 finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza 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 de origen cruzado según el valor del encabezado Origin
:
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Considere el siguiente código que utiliza enrutamiento de punto final para habilitar CORS por punto final utilizando RequireCors
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Observe que solo el /echo
punto de conexión está utilizando el RequireCors
para permitir solicitudes de origen cruzado utilizando la directiva especificada. Los controladores siguientes habilitan CORS mediante el atributo [EnableCors].
A continuación TodoItems1Controller
se proporcionan los 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("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se 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] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
El código anterior se puede probar al implementar la muestra en Azure. En la lista desplegable Controlador, selecciona Preparatorio y después Configurar controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo habilitar CORS en una aplicación de ASP.NET Core.
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 CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
Vea o descargue el código de ejemplo (cómo descargarlo)
Dos 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 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 diferenteExisten tres formas de habilitar CORS:
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Aviso
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de 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,
policy =>
{
policy.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:
_myAllowSpecificOrigins
. El nombre de la política es arbitrario._myAllowSpecificOrigins
directiva 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.WithOrigins
, se describen más adelante en este artículo._myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos.Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
La AddCors llamada al método añade 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,
policy =>
{
policy.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 obtener más información, consulte Opciones de directiva 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,
policy =>
{
policy.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 URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
Aviso
UseCors
debe colocarse después UseRouting
y antes de UseAuthorization
. Esto es para asegurarse de que los encabezados CORS se incluyen en la respuesta para las llamadas autorizadas y no autorizadas.
Normalmente, UseStaticFiles
se llama a antes de UseCors
. Las aplicaciones que usan JavaScript para recuperar archivos estáticos entre sitios deben llamar a UseCors
antes de UseStaticFiles
.
El código resaltado siguiente habilita la directiva CORS predeterminada:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.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.
La habilitación de CORS por punto de conexión usando RequireCors
no admite solicitudes preparatorias automáticas. Para obtener más información, consulte este problema de GitHub y Pruebe CORS con enrutamiento de puntos de conexión y [HttpOptions].
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando 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,
policy =>
{
policy.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
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada, app.UseCors()
por sí solo no habilita CORS./echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada./echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
Consulte Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre el código de prueba similar al anterior.
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.El atributo [EnableCors]
se puede aplicar a:
PageModel
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, 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 siguiente código crea dos políticas CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.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 obtener el mejor control de la limitación de solicitudes CORS:
[EnableCors("MyPolicy")]
con una directiva con nombre.El código de la sección siguiente cumple la lista anterior.
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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:
"MyPolicy"
directiva CORS para el controlador.GetValues2
método .Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
AddPolicy es llamado por Program.cs
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
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 preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de 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,
policy =>
{
policy.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,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria 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 la solicitud entre orígenes.
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte 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 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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
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
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a AllowCredentials:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud 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 el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
Accept
, Accept-Language
, Content-Language
, Content-Type
o Last-Event-ID
.Content-Type
, si está configurado, tiene uno de los siguientes valores: application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test 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 preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
User-Agent
.Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más informaciónPara permitir encabezados específicos, llame a WithHeaders:
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
"*"
Accept
, Content-Type
y Origin
, además de los encabezados personalizados que quiera admitir.Cuando se aplica la directiva CORS:
app.UseCors
en Program.cs
.[EnableCors]
.ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes automáticas preparatorias.
La sección CORS de prueba de este documento muestra este comportamiento.
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS. En algunos escenarios, es posible que esto no sea el caso. Por ejemplo, usando CORS con enrutamiento de punto final .
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 Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre cómo probar el código anterior.
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a SetPreflightMaxAge:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
El siguiente es un ejemplo de una solicitud de origen cruzado desde el botón de prueba Valores a https://cors1.azurewebsites.net/api/values
. El encabezado Origin
:
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 las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, la solicitud del botónejemplo, Eliminar [EnableCors]OPTIONS
desplegada 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 valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador 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.
Las solicitudes a un punto de conexión usando HTTP que son redirigidas a HTTPS por UseHttpsRedirection fallan con ERR_INVALID_REDIRECT on the CORS preflight request
.
Los proyectos de API pueden rechazar solicitudes HTTP en lugar de usar UseHttpsRedirection
para redirigir solicitudes a HTTPS.
De forma predeterminada, los exploradores Chrome y Edge no muestran las 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
Firefox muestra las solicitudes OPTIONS de forma predeterminada.
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows 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.
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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();
Aviso
WithOrigins("https://localhost:<port>");
solo se debería 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 métodos siguientes:
dotnet run
utilizando la URL predeterminada de https://localhost:5001
.https://localhost:44398
.Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES 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. Dependiendo del navegador, se muestra un error similar al siguiente:
El acceso a la obtención en 'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen 'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente 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 finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza 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 de origen cruzado según el valor del encabezado Origin
:
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes preparatorias automáticas . Considere el siguiente código que utiliza enrutamiento de punto final para habilitar CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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 los 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);
}
Prueba el código anterior de la página de prueba (https://cors1.azurewebsites.net/test?number=1
) de la muestra implementada.
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se 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);
}
El código anterior se puede probar al implementar la muestra en Azure. En la lista desplegable Controlador, selecciona Preparatorio y después Configurar controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Por Rick Anderson y Kirk Larkin
En este artículo se muestra cómo habilitar CORS en una aplicación de ASP.NET Core.
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 CORS de Mozilla.
Intercambio de recursos de origen cruzado (CORS):
Vea o descargue el código de ejemplo (cómo descargarlo)
Dos 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 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 diferenteExisten tres formas de habilitar CORS:
El uso del atributo [EnableCors] con una directiva con nombre proporciona el mejor control para limitar los puntos finales que admiten CORS.
Aviso
UseCors debe llamarse en el orden correcto. Para obtener más información vea Orden de Middleware. Por ejemplo, UseCors
debe llamarse antes de UseResponseCaching cuando se utiliza UseResponseCaching
.
Cada ennfoque se detalla en las secciones siguientes.
El middleware de CORS controla las solicitudes entre orígenes. El código siguiente aplica una directiva de 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,
policy =>
{
policy.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:
_myAllowSpecificOrigins
. El nombre de la política es arbitrario._myAllowSpecificOrigins
directiva 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.WithOrigins
, se describen más adelante en este artículo._myAllowSpecificOrigins
directiva de CORS para todos los puntos de conexión del controlador. Consulte enrutamiento de punto final para aplicar una directiva CORS a puntos finales específicos.Con el enrutamiento de puntos de conexión, el middleware de CORS debe configurarse para ejecutarse entre las llamadas a UseRouting
y UseEndpoints
.
Consulte Prueba CORS para obtener instrucciones sobre el código de prueba similar al código anterior.
La AddCors llamada al método añade 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,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
Para obtener más información, consulte Opciones de directiva 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,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
Nota: La URL especificada no debe contener una barra diagonal (/
). Si la dirección URL finaliza con /
, la comparación devuelve false
y no se devuelve ningún encabezado.
El código resaltado siguiente habilita la directiva CORS predeterminada:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.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.
La habilitación de CORS por punto de conexión usando RequireCors
no admite solicitudes preparatorias automáticas. Para obtener más información, consulte este problema de GitHub y Pruebe CORS con enrutamiento de puntos de conexión y [HttpOptions].
Con el enrutamiento de punto final, CORS se puede habilitar por punto final utilizando 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,
policy =>
{
policy.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
agrega el middleware CORS. Debido a que no se ha configurado una política predeterminada, app.UseCors()
por sí solo no habilita CORS./echo
y puntos de conexión del controlador permiten solicitudes entre orígenes mediante la directiva especificada./echo2
y Razor puntos de conexión de páginas no permiten solicitudes de origen cruzado porque no se especificó ninguna política predeterminada.El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final con RequireCors
.
Consulte Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre el código de prueba similar al anterior.
Habilitar CORS con el atributo [EnableCors] y aplicar una directiva con nombre solo a los puntos de conexión que requieren CORS proporcionan el mejor control.
El atributo [EnableCors] proporciona una alternativa a aplicar CORS globalmente. El [EnableCors]
atributo habilita CORS para los puntos de conexión seleccionados, en lugar de todos los puntos de conexión:
[EnableCors]
especifica la política predeterminada.[EnableCors("{Policy String}")]
especifica una directiva guardada.El atributo [EnableCors]
se puede aplicar a:
PageModel
Se pueden aplicar diferentes directivas a controladores, modelos de página o métodos de acción con el atributo [EnableCors]
. Cuando el atributo [EnableCors]
se aplica a un controlador, modelo de página o método de acción, y CORS está habilitado en middleware, se aplican ambas directivas. Se recomienda combinar directivas. Use el el atributo o middleware [EnableCors]
, 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 siguiente código crea dos políticas 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",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.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 obtener el mejor control de la limitación de solicitudes CORS:
[EnableCors("MyPolicy")]
con una directiva con nombre.El código de la sección siguiente cumple la lista anterior.
Consulte Prueba CORS para obtener instrucciones sobre el código de prueba similar al código anterior.
El atributo [DisableCors]no deshabilita el CORS que ha sido habilitado por el enrutamiento de punto final .
El siguiente código define la política CORS "MyPolicy"
:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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:
"MyPolicy"
directiva CORS para el controlador.GetValues2
método .Consulte Probar CORS para obtener instrucciones sobre cómo probar el código anterior.
En esta sección se describen las distintas opciones que se pueden establecer en una directiva de CORS:
AddPolicy es llamado por Startup.ConfigureServices
. Para algunas opciones, puede resultar útil leer primero la sección Funcionamiento de CORS .
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 preparatorias y al Access-Control-Allow-Origin
encabezado. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
SetIsOriginAllowedToAllowWildcardSubdomains: establece la IsOriginAllowed propiedad de la directiva en una función que permite que los orígenes coincidan con un dominio comodín configurado al evaluar si se permite el origen.
options.AddPolicy("MyAllowSubdomainPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
Access-Control-Allow-Methods
. Para obtener más información, consulte la sección Solicitudes de prelanzamiento .Para permitir que se envíen encabezados específicos en una solicitud CORS, llamada encabezados de solicitud de autor , llame a WithHeaders y especifique los encabezados permitidos:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
AllowAnyHeader
afecta a las solicitudes de preventa y al encabezado Access-Control-Request-Headers . Para obtener más información, consulte la sección Solicitudes de prelanzamiento .
Una coincidencia de política de CORS Middleware con encabezados específicos especificados por WithHeaders
solo es posible cuando los encabezados enviados en Access-Control-Request-Headers
coinciden exactamente con los encabezados indicados en WithHeaders
.
Por ejemplo, considere la posibilidad de configurar una aplicación como se indica a continuación:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware rechaza la solicitud preparatoria 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 la solicitud entre orígenes.
De forma predeterminada, el explorador no expone todos los encabezados de respuesta a la aplicación. Para obtener más información, consulte 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 de respuesta simples. Para que otros encabezados estén disponibles para la aplicación, llame a WithExposedHeaders:
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
Las credenciales requieren un control especial en una solicitud CORS. De forma predeterminada, el navegador no envía credenciales con una solicitud de origen cruzado. Las credenciales incluyen cookies y esquemas de autenticación HTTP. Para enviar credenciales con una solicitud de origen cruzado, el cliente debe establecer XMLHttpRequest.withCredentials
en true
.
Uso XMLHttpRequest
directo:
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
}
});
Uso de la Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
El servidor debe permitir las credenciales. Para permitir credenciales de origen cruzado, llame a AllowCredentials:
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
La respuesta HTTP incluye un encabezado Access-Control-Allow-Credentials
, que le dice al navegador que el servidor permite credenciales para una solicitud de origen cruzado.
Si el explorador envía credenciales, pero la respuesta no incluye un encabezado válido Access-Control-Allow-Credentials
, el explorador no expone la respuesta a la aplicación y se produce un error en la solicitud 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 el conocimiento del usuario.
La especificación CORS también indica que establecer orígenes en "*"
(todos los orígenes) no es válido si el Access-Control-Allow-Credentials
encabezado está presente.
Para algunas solicitudes CORS, el navegador envía una solicitud adicional de OPTIONS antes de realizar la solicitud real. Esta solicitud se denomina solicitud preparatoria. El navegador puede omitir la solicitud preparatoria si se cumplen todas las siguientes condiciones:
Accept
, Accept-Language
, Content-Language
, Content-Type
o Last-Event-ID
.Content-Type
, si está configurado, tiene uno de los siguientes valores: application/x-www-form-urlencoded
multipart/form-data
text/plain
La regla sobre encabezados de solicitud establecida para la solicitud del cliente se aplica a los encabezados que la aplicación establece llamando a setRequestHeader
en el objeto XMLHttpRequest
. La especificación CORS llama a estos encabezados de solicitud de autor. La regla no se aplica a los encabezados que el explorador puede establecer, como User-Agent
, Host
o Content-Length
.
La siguiente es una respuesta de ejemplo similar a la solicitud preparatoria realizada desde el botón [Put test] en la sección Test 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 preparatoria utiliza el método OPCIONES HTTP . Puede incluir los siguientes encabezados:
User-Agent
.Si se rechaza la solicitud de preventa, la aplicación devuelve una respuesta 200 OK
pero no establece los encabezados CORS. Por lo tanto, el explorador no intenta la solicitud entre orígenes. Para obtener un ejemplo de una solicitud preparatoria denegada, consulte la sección Probar CORS de este documento.
Con las herramientas F12, la aplicación de consola muestra un error similar a uno de los siguientes, según el explorador:
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motivo: la solicitud CORS no se realizó correctamente). Más informaciónPara permitir encabezados específicos, llame a WithHeaders:
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.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",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
Los navegadores no son consistentes en la forma en que configuran Access-Control-Request-Headers
. Si alguna de las dos opciones:
"*"
Accept
, Content-Type
y Origin
, además de los encabezados personalizados que quiera admitir.Cuando se aplica la directiva CORS:
app.UseCors
en Startup.Configure
.[EnableCors]
.ASP.NET Core responde a la solicitud preparatoria de OPTIONS.
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes automáticas preparatorias.
La sección CORS de prueba de este documento muestra este comportamiento.
Cuando CORS está habilitado con la directiva adecuada, ASP.NET Core suele responder automáticamente a las solicitudes previas de CORS. En algunos escenarios, es posible que esto no sea el caso. Por ejemplo, usando CORS con enrutamiento de punto final .
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 Prueba CORS con enrutamiento de punto final y [HttpOptions] para obtener instrucciones sobre cómo probar el código anterior.
El encabezado Access-Control-Max-Age
especifica cuánto tiempo se puede almacenar en caché la respuesta a la solicitud preparatoria. Para establecer este encabezado, llame a SetPreflightMaxAge:
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
Esta sección describe lo que sucede en una solicitud CORS a nivel de los mensajes HTTP.
<script>
para recibir la respuesta. Se permite cargar scripts entre orígenes.La especificación CORS introdujo varios encabezados HTTP nuevos que permiten solicitudes de origen cruzado. Si un explorador admite CORS, establece estos encabezados automáticamente para las solicitudes entre orígenes. El código JavaScript personalizado no es necesario para habilitar CORS.
El siguiente es un ejemplo de una solicitud de origen cruzado desde el botón de prueba Valores a https://cors1.azurewebsites.net/api/values
. El encabezado Origin
:
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 las solicitudes OPTIONS
, el servidor establece el encabezado Encabezados de respuesta Access-Control-Allow-Origin: {allowed origin}
en la respuesta. Por ejemplo, la solicitud del botónejemplo, Eliminar [EnableCors]OPTIONS
desplegada 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 valor https://cors1.azurewebsites.net
de este encabezado coincide con el encabezado Origin
de la solicitud.
Si se llama aAllowAnyOrigin, el Access-Control-Allow-Origin: *
se devuelve el valor comodín . AllowAnyOrigin
permite cualquier origen.
Si la respuesta no incluye el encabezado Access-Control-Allow-Origin
, la solicitud de origen cruzado falla. En concreto, el navegador 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.
De forma predeterminada, los exploradores Chrome y Edge no muestran las 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
Firefox muestra las solicitudes OPTIONS de forma predeterminada.
Al implementar en IIS, CORS debe ejecutarse antes de la autenticación de Windows 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.
La descarga de muestra tiene código para probar CORS. Vea cómo descargarlo. El ejemplo es un proyecto de API con Razor páginas agregadas:
public class StartupTest2
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.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();
});
}
}
Aviso
WithOrigins("https://localhost:<port>");
solo se debería 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 métodos siguientes:
dotnet run
utilizando la URL predeterminada de https://localhost:5001
.https://localhost:44398
.Uso de un explorador con las herramientas F12:
Seleccione el botón Valores y revise los encabezados en la pestaña Red.
Seleccione el botón probar PUT. Consulte Mostrar solicitudes de OPCIONES para obtener instrucciones sobre cómo mostrar la solicitud de OPCIONES. La prueba PUT crea dos solicitudes, una solicitud preparatoria de OPCIONES 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. Dependiendo del navegador, se muestra un error similar al siguiente:
El acceso a la obtención en 'https://cors1.azurewebsites.net/api/values/GetValues2'
desde el origen 'https://cors3.azurewebsites.net'
ha sido bloqueado por la política de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente 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 finales habilitados para CORS se pueden probar con una herramienta, como curl o Fiddler. Cuando se utiliza 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 de origen cruzado según el valor del encabezado Origin
:
El siguiente comando usa curl
para emitir una solicitud OPTIONS con información:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Habilitar CORS por punto final usando RequireCors
actualmente no admite solicitudes preparatorias automáticas . Considere el siguiente código que utiliza enrutamiento de punto final 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,
policy =>
{
policy.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 los 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);
}
Prueba el código anterior de la página de prueba (https://cors1.azurewebsites.net/test?number=1
) de la muestra implementada.
Los botones Delete [EnableCors] y GET [EnableCors] tienen éxito, porque los puntos finales tienen [EnableCors]
y responden a las solicitudes preparatorias. Se produce un error en los demás puntos de conexión. El botón GET falla, porque el JavaScript envía:
headers: {
"Content-Type": "x-custom-header"
},
A continuación TodoItems2Controller
se proporcionan puntos de conexión similares, pero se 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);
}
El código anterior se puede probar al implementar la muestra en Azure. En la lista desplegable Controlador, selecciona Preparatorio y después Configurar controlador. Todas las llamadas CORS a los puntos finales TodoItems2Controller
tienen éxito.
Comentarios de ASP.NET Core
ASP.NET Core é un proxecto de código aberto. Selecciona unha ligazón para ofrecer comentarios:
Evento
Campionato do Mundo de Power BI DataViz
Feb 14, 4 PM - Mar 31, 4 PM
Con 4 posibilidades de entrar, poderías gañar un paquete de conferencias e facelo ao Live Grand Finale en Las Vegas
Máis informaciónFormación
Camiño de aprendizaxe
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
Documentación
Habilitación de solicitudes entre orígenes en ASP.NET Web API 2
Muestra cómo admitir el uso compartido de recursos entre orígenes (CORS) en ASP.NET API web.
Lista segura de IP de cliente para ASP.NET Core
Obtenga información sobre cómo escribir filtros de acciones o middleware para validar direcciones IP remotas en una lista de direcciones IP aprobadas.
Middleware de reescritura de URL en ASP.NET Core
Aprenda a reescribir y redireccionar URL con el middleware de reescritura de URL en aplicaciones ASP.NET Core.