Partilhar via


Habilitar o CORS (solicitações entre origens) no ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Por Rick Anderson e Kirk Larkin

Este artigo mostra como o Compartilhamento de Recursos Entre Origens (CORS) é habilitado em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que ofereceu a página da Web. Essa restrição é chamada de política de mesma origem. Essa restrição se chama política da mesma origem e impede que um site mal-intencional leia dados confidenciais de outro site. Às vezes, talvez você queira permitir que outros sites façam solicitações entre origens para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

CORS (compartilhamento de recursos entre origens):

  • É um padrão W3C que permite que um servidor relaxe a política de mesma origem.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura permitindo CORS. Para saber mais, veja Como funciona CORS.
  • Permite que um servidor autorize explicitamente algumas solicitações de origem cruzada e rejeite outras.
  • É mais seguro e flexível do que técnicas anteriores, como JSONP.

Exibir ou baixar código de exemplo (como baixar)

Mesma origem

Duas URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Essas duas URLs têm a mesma origem:

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

Essas URLs têm origens diferentes das duas URLs anteriores:

  • https://example.net: Domínio diferente
  • https://contoso.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porta diferente

Habilitar CORS

Há três maneiras de habilitar CORS:

O uso do atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que dão suporte ao CORS.

Aviso

UseCors deve ser chamado na ordem correta. Para obter mais informações, veja ordem do Middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usando UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O Middleware CORS lida com solicitações entre origens. O código a seguir aplica uma política CORS a todos os pontos de extremidade do aplicativo com as origens especificadas:

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

O código anterior:

Com o roteamento de ponto de extremidade, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

A chamada do método AddCors adiciona serviços CORS ao contêiner de serviços do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os métodos CorsPolicyBuilder podem ser encadeados, conforme mostrado no código a seguir:

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

Observação: a URL especificada não deve conter uma barra à direita (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

Ordem de UseCors e UseStaticFiles

Normalmente, UseStaticFiles é chamado antes de UseCors. Os aplicativos que usam JavaScript para recuperar arquivos estáticos entre sites devem chamar UseCors antes de UseStaticFiles.

CORS com política padrão e middleware

O código realçado a seguir habilita a política padrão do CORS:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto de extremidade

Com o roteamento de ponto de extremidade, o CORS pode ser habilitado por ponto de extremidade usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors adiciona o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() por si só não habilita o CORS.
  • Os pontos de extremidade do controlador e /echo permitem solicitações entre origens usando a política especificada.
  • Os pontos de extremidade /echo2 e Razor páginas não permitem solicitações entre origens porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

Consulte Testar CORS com o atributo [EnableCors] e o método RequireCors para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar o CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa para aplicar o CORS globalmente. O atributo [EnableCors] habilita o CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] especifica a política padrão.
  • [EnableCors("{Policy String}")] especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o atributo [EnableCors]. Quando o atributo [EnableCors] é aplicado a um controlador, modelo de página ou método de ação e o CORS está habilitado no middleware, ambas as políticas são aplicadas. É recomendável não combinar políticas. Use o [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política 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(),
        };
    }
}

O código a seguir cria duas 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 obter o melhor controle de limitação de solicitações CORS:

O código na próxima seção atende à lista anterior.

Desabilitar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a 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();

O código a seguir desabilita o CORS para a ação GetValues2:

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

}

O código anterior:

Consulte Testar CORS para obter instruções sobre como testar código semelhante ao código anterior.

Opções de política CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Program.cs. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS.

Definir as origens permitidas

AllowAnyOrigin: permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin não é seguro porque qualquer site pode fazer solicitações entre origens para o aplicativo.

Observação

A especificação de AllowAnyOrigin e AllowCredentials não é uma configuração segura e pode resultar em uma solicitação intersite forjada. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com os dois métodos.

AllowAnyOrigin afeta as solicitações de pré-vôo e o Access-Control-Allow-Origin cabeçalho. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

SetIsOriginAllowedToAllowWildcardSubdomains: define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Allow-Methods. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamado cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação de autor, chame 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 afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Uma correspondência da política do CORS Middleware com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondem exatamente aos cabeçalhos declarados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O MIDDLEware cors recusa uma solicitação de pré-vôo com o cabeçalho de solicitação a seguir porque Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK, mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação entre origens.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte Compartilhamento de recursos entre origens do W3C (Terminologia): cabeçalho de resposta simples.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame 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();

Credenciais em solicitações entre origens

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação entre origens, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando jQuery:

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

Usando a API Fetch:

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

O servidor deve permitir as credenciais. Para permitir credenciais entre origens, chame 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();

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação entre origens.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials, o navegador não exporá a resposta ao aplicativo e a solicitação entre origens falhará.

Permitir credenciais entre origens é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválida se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Solicitações de pré-voo

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação de pré-voo. O navegador poderá ignorar a solicitação de pré-vôo se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type ou Last-Event-ID.
  • O cabeçalho Content-Type, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra nos cabeçalhos de solicitação definida para a solicitação do cliente se aplica aos cabeçalhos que o aplicativo define chamando setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Host ou Content-Length.

Observação

Este artigo contém URLs criadas com a implantação do código de exemplo em dois sites do Azure, https://cors3.azurewebsites.net e https://cors.azurewebsites.net.

Veja a seguir um exemplo de resposta semelhante à solicitação de pré-voo feita no botão [Colocar teste] na seção Testar CORS deste 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

A solicitação de pré-vôo usa o método HTTP OPTIONS. Ele pode incluir os seguintes cabeçalhos:

  • Access-Control-Request-Method: o método HTTP que será usado para a solicitação real.
  • Access-Control-Request-Headers: uma lista de cabeçalhos de solicitação que o aplicativo define na solicitação real. Conforme indicado anteriormente, isso não inclui cabeçalhos que o navegador define, como User-Agent.

Se a solicitação de pré-voo for negada, o aplicativo retornará uma resposta 200 OK, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação entre origens. Para obter um exemplo de uma solicitação de pré-voo negada, consulte a seção Testar CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: solicitação entre origens bloqueada: a mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: a solicitação CORS não foi bem-sucedida). Saiba mais
  • Chromium baseado: o acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política cors: a resposta à solicitação de pré-vôo não passa o controle de acesso marcar: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação de autor, chame 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();

Os navegadores não são consistentes em como eles definem Access-Control-Request-Headers. Se um dos dois:

  • Cabeçalhos são definidos como qualquer outra coisa que não seja "*"
  • AllowAnyHeader é chamado: inclua pelo menos Accept, Content-Type e Origin, além de todos os cabeçalhos personalizados que você deseja dar suporte.

Código de solicitação de pré-vôo automático

Quando a política CORS é aplicada:

  • Globalmente chamando app.UseCors em Program.cs.
  • Usando o atributo [EnableCors].

ASP.NET Core responde à solicitação OPTIONS de pré-vôo.

A seção Testar CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para solicitações de pré-voo

Quando o CORS está habilitado com a política apropriada, ASP.NET Core geralmente responde às solicitações de pré-vôo CORS automaticamente.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Testar CORS com o atributo [EnableCors] e o método RequireCors para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração do pré-vôo

O cabeçalho Access-Control-Max-Age especifica por quanto tempo a resposta à solicitação de pré-voo pode ser armazenada em cache. Para definir esse cabeçalho, chame 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();

Habilitar o CORS em um ponto de extremidade

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um recurso de segurança. O CORS é um padrão W3C que permite ao servidor flexibilizar a política de same-origin.
    • Por exemplo, um ator mal-intencionado pode usar o XSS (Script entre Sites) em seu site e executar uma solicitação entre sites no site habilitado para CORS para roubar informações.
  • Uma API não é mais segura permitindo o CORS.
    • Cabe ao cliente (navegador) impor o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem uma solicitação de API XHR ou Fetch de origem cruzada que, de outra forma, seria proibida.
    • Navegadores sem CORS não podem fazer solicitações entre origens. Antes do CORS, JSONP era usado para contornar essa restrição. O JSONP não usa XHR, ele usa a marca <script> para receber a resposta. Os scripts têm permissão para serem carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que habilitam solicitações entre origens. Se um navegador der suporte ao CORS, ele definirá esses cabeçalhos automaticamente para solicitações entre origens. O código JavaScript personalizado não é necessário para habilitar o CORS.

Veja a seguir um exemplo de uma solicitação entre origens do botão de teste Valores para https://cors1.azurewebsites.net/api/values. O cabeçalho Origin:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É necessário e deve ser diferente do host.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Em solicitações OPTIONS, o servidor define o cabeçalho Cabeçalhos de resposta Access-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, no código de exemplo, a solicitação OPTIONS de botão Delete [EnableCors] contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O valor https://cors1.azurewebsites.net desse cabeçalho corresponde ao cabeçalho Origin da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, será retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o cabeçalho Access-Control-Allow-Origin, a solicitação entre origens falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibiliza a resposta para o aplicativo cliente.

O redirecionamento HTTP para HTTPS causa ERR_INVALID_REDIRECT na solicitação de simulação do CORS

Solicitações para um ponto de extremidade usando HTTP que são redirecionadas para HTTPS por falha de UseHttpsRedirection com ERR_INVALID_REDIRECT on the CORS preflight request.

Os projetos de API podem rejeitar solicitações HTTP em vez de usar UseHttpsRedirection para redirecionar solicitações para HTTPS.

CORS no IIS

Ao implantar no IIS, o CORS precisará ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir o acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Testar o CORS

O download de exemplo tem código para testar o CORS. Consulte como baixar. O exemplo é um projeto de API com Razor Pages (Páginas) adicionado:

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>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os pontos de extremidade para teste:

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

}

MyDisplayRouteInfo é fornecido pelo pacote NuGet Rick.Docs.Samples.RouteInfo e exibe as informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e examine os cabeçalhos na guia Rede.

  • Selecione o botão de teste PUT. Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações, uma solicitação de pré-vôo OPTIONS e a solicitação PUT.

  • Selecione o botão GetValues2 [DisableCors] para disparar uma solicitação CORS com falha. Conforme mencionado no documento, a resposta retorna 200 êxitos, mas a solicitação CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso para buscar na 'https://cors1.azurewebsites.net/api/values/GetValues2' origem 'https://cors3.azurewebsites.net' foi bloqueado pela política do CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Os pontos de extremidade habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo cabeçalho Origin deve ser diferente do host que recebe a solicitação. Se a solicitação não for entre origens com base no valor do cabeçalho Origin:

  • Não é necessário que o Middleware CORS processe a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir usa curl para emitir uma solicitação OPTIONS com informações:

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

Testar o CORS com o atributo [EnableCors] e o método RequireCors

Considere o código a seguir que usa o roteamento de ponto de extremidade para habilitar o CORS por ponto de extremidade usando 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 apenas o ponto de extremidade /echo está usando o RequireCors para permitir solicitações entre origens usando a política especificada. Os controladores abaixo habilitam o CORS usando o atributo [EnableCors].

O seguinte TodoItems1Controller fornece pontos de extremidade para teste:

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

Os botões Excluir [EnableCors] e GET [EnableCors] têm êxito, pois os pontos de extremidade têm [EnableCors] e respondem a solicitações de simulação. Os outros pontos de extremidade falham. O botão GET falha porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece pontos de extremidade semelhantes, mas inclui código explícito para responder a solicitações 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);
}

O código anterior pode ser testado implantando o exemplo no Azure. Na lista suspensa Controlador, selecione Simular e, em seguida , Definir Controlador. Todas as chamadas CORS para os pontos de extremidade TodoItems2Controller são bem-sucedidas.

Recursos adicionais

Por Rick Anderson e Kirk Larkin

Este artigo mostra como habilitar o CORS em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que ofereceu a página da Web. Essa restrição é chamada de política de mesma origem. Essa restrição se chama política da mesma origem e impede que um site mal-intencional leia dados confidenciais de outro site. Às vezes, talvez você queira permitir que outros sites façam solicitações entre origens para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

CORS (compartilhamento de recursos entre origens):

  • É um padrão W3C que permite que um servidor relaxe a política de mesma origem.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura permitindo CORS. Para saber mais, veja Como funciona CORS.
  • Permite que um servidor autorize explicitamente algumas solicitações de origem cruzada e rejeite outras.
  • É mais seguro e flexível do que técnicas anteriores, como JSONP.

Exibir ou baixar código de exemplo (como baixar)

Mesma origem

Duas URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Essas duas URLs têm a mesma origem:

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

Essas URLs têm origens diferentes das duas URLs anteriores:

  • https://example.net: Domínio diferente
  • https://www.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porta diferente

Habilitar CORS

Há três maneiras de habilitar CORS:

O uso do atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que dão suporte ao CORS.

Aviso

UseCors deve ser chamado na ordem correta. Para obter mais informações, veja ordem do Middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usando UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O Middleware CORS lida com solicitações entre origens. O código a seguir aplica uma política CORS a todos os pontos de extremidade do aplicativo com as origens especificadas:

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

O código anterior:

Com o roteamento de ponto de extremidade, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

A chamada do método AddCors adiciona serviços CORS ao contêiner de serviços do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os métodos CorsPolicyBuilder podem ser encadeados, conforme mostrado no código a seguir:

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

Observação: a URL especificada não deve conter uma barra à direita (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

Aviso

UseCors deve ser colocado após UseRouting e antes de UseAuthorization. Isso é para garantir que os cabeçalhos CORS sejam incluídos na resposta para chamadas autorizadas e não autorizadas.

Ordem de UseCors e UseStaticFiles

Normalmente, UseStaticFiles é chamado antes de UseCors. Os aplicativos que usam JavaScript para recuperar arquivos estáticos entre sites devem chamar UseCors antes de UseStaticFiles.

CORS com política padrão e middleware

O código realçado a seguir habilita a política padrão do CORS:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto de extremidade

Com o roteamento de ponto de extremidade, o CORS pode ser habilitado por ponto de extremidade usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors adiciona o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() por si só não habilita o CORS.
  • Os pontos de extremidade do controlador e /echo permitem solicitações entre origens usando a política especificada.
  • Os pontos de extremidade /echo2 e Razor páginas não permitem solicitações entre origens porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

No ASP.NET Core 7.0, o atributo [EnableCors] deve passar um parâmetro ou um aviso ASP0023 é gerado a partir de uma correspondência ambígua na rota. ASP.NET Core 8.0 e posterior não gera o aviso ASP0023.

[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 Testar CORS com o atributo [EnableCors] e o método RequireCors para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar o CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa para aplicar o CORS globalmente. O atributo [EnableCors] habilita o CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] especifica a política padrão.
  • [EnableCors("{Policy String}")] especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o atributo [EnableCors]. Quando o atributo [EnableCors] é aplicado a um controlador, modelo de página ou método de ação e o CORS está habilitado no middleware, ambas as políticas são aplicadas. É recomendável não combinar políticas. Use o [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política 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(),
        };
    }
}

O código a seguir cria duas 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 obter o melhor controle de limitação de solicitações CORS:

O código na próxima seção atende à lista anterior.

Desabilitar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a 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();

O código a seguir desabilita o CORS para a ação GetValues2:

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

}

O código anterior:

Consulte Testar CORS para obter instruções sobre como testar código semelhante ao código anterior.

Opções de política CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Program.cs. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS.

Definir as origens permitidas

AllowAnyOrigin: permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin não é seguro porque qualquer site pode fazer solicitações entre origens para o aplicativo.

Observação

A especificação de AllowAnyOrigin e AllowCredentials não é uma configuração segura e pode resultar em uma solicitação intersite forjada. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com os dois métodos.

AllowAnyOrigin afeta as solicitações de pré-vôo e o Access-Control-Allow-Origin cabeçalho. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

SetIsOriginAllowedToAllowWildcardSubdomains: define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Allow-Methods. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamado cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação de autor, chame 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 afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Uma correspondência da política do CORS Middleware com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondem exatamente aos cabeçalhos declarados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O MIDDLEware cors recusa uma solicitação de pré-vôo com o cabeçalho de solicitação a seguir porque Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK, mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação entre origens.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte Compartilhamento de recursos entre origens do W3C (Terminologia): cabeçalho de resposta simples.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame 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();

Credenciais em solicitações entre origens

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação entre origens, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando jQuery:

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

Usando a API Fetch:

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

O servidor deve permitir as credenciais. Para permitir credenciais entre origens, chame 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();

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação entre origens.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials, o navegador não exporá a resposta ao aplicativo e a solicitação entre origens falhará.

Permitir credenciais entre origens é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválida se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Solicitações de pré-voo

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação de pré-voo. O navegador poderá ignorar a solicitação de pré-vôo se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type ou Last-Event-ID.
  • O cabeçalho Content-Type, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra nos cabeçalhos de solicitação definida para a solicitação do cliente se aplica aos cabeçalhos que o aplicativo define chamando setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Host ou Content-Length.

Veja a seguir um exemplo de resposta semelhante à solicitação de pré-voo feita no botão [Colocar teste] na seção Testar CORS deste 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

A solicitação de pré-vôo usa o método HTTP OPTIONS. Ele pode incluir os seguintes cabeçalhos:

Se a solicitação de pré-voo for negada, o aplicativo retornará uma resposta 200 OK, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação entre origens. Para obter um exemplo de uma solicitação de pré-voo negada, consulte a seção Testar CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: solicitação entre origens bloqueada: a mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: a solicitação CORS não foi bem-sucedida). Saiba mais
  • Chromium baseado: o acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política cors: a resposta à solicitação de pré-vôo não passa o controle de acesso marcar: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação de autor, chame 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();

Os navegadores não são consistentes em como eles definem Access-Control-Request-Headers. Se um dos dois:

  • Cabeçalhos são definidos como qualquer outra coisa que não seja "*"
  • AllowAnyHeader é chamado: inclua pelo menos Accept, Content-Type e Origin, além de todos os cabeçalhos personalizados que você deseja dar suporte.

Código de solicitação de pré-vôo automático

Quando a política CORS é aplicada:

  • Globalmente chamando app.UseCors em Program.cs.
  • Usando o atributo [EnableCors].

ASP.NET Core responde à solicitação OPTIONS de pré-vôo.

A seção Testar CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para solicitações de pré-voo

Quando o CORS está habilitado com a política apropriada, ASP.NET Core geralmente responde às solicitações de pré-vôo CORS automaticamente.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Testar CORS com o atributo [EnableCors] e o método RequireCors para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração do pré-vôo

O cabeçalho Access-Control-Max-Age especifica por quanto tempo a resposta à solicitação de pré-voo pode ser armazenada em cache. Para definir esse cabeçalho, chame 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();

Habilitar o CORS em um ponto de extremidade

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um recurso de segurança. O CORS é um padrão W3C que permite ao servidor flexibilizar a política de same-origin.
    • Por exemplo, um ator mal-intencionado pode usar o XSS (Script entre Sites) em seu site e executar uma solicitação entre sites no site habilitado para CORS para roubar informações.
  • Uma API não é mais segura permitindo o CORS.
    • Cabe ao cliente (navegador) impor o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem uma solicitação de API XHR ou Fetch de origem cruzada que, de outra forma, seria proibida.
    • Navegadores sem CORS não podem fazer solicitações entre origens. Antes do CORS, JSONP era usado para contornar essa restrição. O JSONP não usa XHR, ele usa a marca <script> para receber a resposta. Os scripts têm permissão para serem carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que habilitam solicitações entre origens. Se um navegador der suporte ao CORS, ele definirá esses cabeçalhos automaticamente para solicitações entre origens. O código JavaScript personalizado não é necessário para habilitar o CORS.

Clique no botão de teste PUT no exemplo implantado. O cabeçalho Origin:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É necessário e deve ser diferente do host.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Em solicitações OPTIONS, o servidor define o cabeçalho Cabeçalhos de resposta Access-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, no código de exemplo, a solicitação OPTIONS de botão Delete [EnableCors] contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O valor https://cors1.azurewebsites.net desse cabeçalho corresponde ao cabeçalho Origin da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, será retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o cabeçalho Access-Control-Allow-Origin, a solicitação entre origens falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibiliza a resposta para o aplicativo cliente.

O redirecionamento HTTP para HTTPS causa ERR_INVALID_REDIRECT na solicitação de simulação do CORS

Solicitações para um ponto de extremidade usando HTTP que são redirecionadas para HTTPS por falha de UseHttpsRedirection com ERR_INVALID_REDIRECT on the CORS preflight request.

Os projetos de API podem rejeitar solicitações HTTP em vez de usar UseHttpsRedirection para redirecionar solicitações para HTTPS.

CORS no IIS

Ao implantar no IIS, o CORS precisará ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir o acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Testar o CORS

O download de exemplo tem código para testar o CORS. Consulte como baixar. O exemplo é um projeto de API com Razor Pages (Páginas) adicionado:

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>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os pontos de extremidade para teste:

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

}

MyDisplayRouteInfo é fornecido pelo pacote NuGet Rick.Docs.Samples.RouteInfo e exibe as informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e examine os cabeçalhos na guia Rede.

  • Selecione o botão de teste PUT. Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações, uma solicitação de pré-vôo OPTIONS e a solicitação PUT.

  • Selecione o botão GetValues2 [DisableCors] para disparar uma solicitação CORS com falha. Conforme mencionado no documento, a resposta retorna 200 êxitos, mas a solicitação CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso para buscar na 'https://cors1.azurewebsites.net/api/values/GetValues2' origem 'https://cors3.azurewebsites.net' foi bloqueado pela política do CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Os pontos de extremidade habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo cabeçalho Origin deve ser diferente do host que recebe a solicitação. Se a solicitação não for entre origens com base no valor do cabeçalho Origin:

  • Não é necessário que o Middleware CORS processe a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir usa curl para emitir uma solicitação OPTIONS com informações:

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

Testar o CORS com o atributo [EnableCors] e o método RequireCors

Considere o código a seguir que usa o roteamento de ponto de extremidade para habilitar o CORS por ponto de extremidade usando 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 apenas o ponto de extremidade /echo está usando o RequireCors para permitir solicitações entre origens usando a política especificada. Os controladores abaixo habilitam o CORS usando o atributo [EnableCors].

O seguinte TodoItems1Controller fornece pontos de extremidade para teste:

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

Os botões Excluir [EnableCors] e GET [EnableCors] têm êxito, pois os pontos de extremidade têm [EnableCors] e respondem a solicitações de simulação. Os outros pontos de extremidade falham. O botão GET falha porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece pontos de extremidade semelhantes, mas inclui código explícito para responder a solicitações 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);
}

O código anterior pode ser testado implantando o exemplo no Azure. Na lista suspensa Controlador, selecione Simulação e, em seguida, Definir Controlador. Todas as chamadas CORS para os pontos de extremidade TodoItems2Controller são bem-sucedidas.

Recursos adicionais

Por Rick Anderson e Kirk Larkin

Este artigo mostra como habilitar o CORS em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que ofereceu a página da Web. Essa restrição é chamada de política de mesma origem. Essa restrição se chama política da mesma origem e impede que um site mal-intencional leia dados confidenciais de outro site. Às vezes, talvez você queira permitir que outros sites façam solicitações entre origens para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

CORS (compartilhamento de recursos entre origens):

  • É um padrão W3C que permite que um servidor relaxe a política de mesma origem.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura permitindo CORS. Para saber mais, veja Como funciona CORS.
  • Permite que um servidor autorize explicitamente algumas solicitações de origem cruzada e rejeite outras.
  • É mais seguro e flexível do que técnicas anteriores, como JSONP.

Exibir ou baixar código de exemplo (como baixar)

Mesma origem

Duas URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Essas duas URLs têm a mesma origem:

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

Essas URLs têm origens diferentes das duas URLs anteriores:

  • https://example.net: Domínio diferente
  • https://www.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porta diferente

Habilitar CORS

Há três maneiras de habilitar CORS:

O uso do atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que dão suporte ao CORS.

Aviso

UseCors deve ser chamado na ordem correta. Para obter mais informações, veja ordem do Middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usando UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O Middleware CORS lida com solicitações entre origens. O código a seguir aplica uma política CORS a todos os pontos de extremidade do aplicativo com as origens especificadas:

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

O código anterior:

Com o roteamento de ponto de extremidade, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

A chamada do método AddCors adiciona serviços CORS ao contêiner de serviços do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os métodos CorsPolicyBuilder podem ser encadeados, conforme mostrado no código a seguir:

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

Observação: a URL especificada não deve conter uma barra à direita (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

Aviso

UseCors deve ser colocado após UseRouting e antes de UseAuthorization. Isso é para garantir que os cabeçalhos CORS sejam incluídos na resposta para chamadas autorizadas e não autorizadas.

Ordem de UseCors e UseStaticFiles

Normalmente, UseStaticFiles é chamado antes de UseCors. Os aplicativos que usam JavaScript para recuperar arquivos estáticos entre sites devem chamar UseCors antes de UseStaticFiles.

CORS com política padrão e middleware

O código realçado a seguir habilita a política padrão do CORS:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto de extremidade

Habilitar o CORS por ponto de extremidade usando RequireCors não oferece suporte a solicitações de simulação automática. Para obter mais informações, confira esta edição do GitHub e Teste o CORS com roteamento de pontos de extremidade e [HttpOptions].

Com o roteamento de ponto de extremidade, o CORS pode ser habilitado por ponto de extremidade usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors adiciona o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() por si só não habilita o CORS.
  • Os pontos de extremidade do controlador e /echo permitem solicitações entre origens usando a política especificada.
  • Os pontos de extremidade /echo2 e Razor páginas não permitem solicitações entre origens porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

Consulte Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar o CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa para aplicar o CORS globalmente. O atributo [EnableCors] habilita o CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] especifica a política padrão.
  • [EnableCors("{Policy String}")] especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o atributo [EnableCors]. Quando o atributo [EnableCors] é aplicado a um controlador, modelo de página ou método de ação e o CORS está habilitado no middleware, ambas as políticas são aplicadas. É recomendável não combinar políticas. Use o [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política 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(),
        };
    }
}

O código a seguir cria duas 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 obter o melhor controle de limitação de solicitações CORS:

O código na próxima seção atende à lista anterior.

Desabilitar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a 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();

O código a seguir desabilita o CORS para a ação GetValues2:

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

}

O código anterior:

Consulte Testar CORS para obter instruções sobre como testar código semelhante ao código anterior.

Opções de política CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Program.cs. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS.

Definir as origens permitidas

AllowAnyOrigin: permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin não é seguro porque qualquer site pode fazer solicitações entre origens para o aplicativo.

Observação

A especificação de AllowAnyOrigin e AllowCredentials não é uma configuração segura e pode resultar em uma solicitação intersite forjada. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com os dois métodos.

AllowAnyOrigin afeta as solicitações de pré-vôo e o Access-Control-Allow-Origin cabeçalho. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

SetIsOriginAllowedToAllowWildcardSubdomains: define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Allow-Methods. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamado cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação de autor, chame 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 afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Uma correspondência da política do CORS Middleware com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondem exatamente aos cabeçalhos declarados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O MIDDLEware cors recusa uma solicitação de pré-vôo com o cabeçalho de solicitação a seguir porque Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK, mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação entre origens.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte Compartilhamento de recursos entre origens do W3C (Terminologia): cabeçalho de resposta simples.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame 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();

Credenciais em solicitações entre origens

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação entre origens, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando jQuery:

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

Usando a API Fetch:

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

O servidor deve permitir as credenciais. Para permitir credenciais entre origens, chame 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();

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação entre origens.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials, o navegador não exporá a resposta ao aplicativo e a solicitação entre origens falhará.

Permitir credenciais entre origens é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválida se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Solicitações de pré-voo

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação de pré-voo. O navegador poderá ignorar a solicitação de pré-vôo se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type ou Last-Event-ID.
  • O cabeçalho Content-Type, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra nos cabeçalhos de solicitação definida para a solicitação do cliente se aplica aos cabeçalhos que o aplicativo define chamando setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Host ou Content-Length.

Veja a seguir um exemplo de resposta semelhante à solicitação de pré-voo feita no botão [Colocar teste] na seção Testar CORS deste 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

A solicitação de pré-vôo usa o método HTTP OPTIONS. Ele pode incluir os seguintes cabeçalhos:

Se a solicitação de pré-voo for negada, o aplicativo retornará uma resposta 200 OK, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação entre origens. Para obter um exemplo de uma solicitação de pré-voo negada, consulte a seção Testar CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: solicitação entre origens bloqueada: a mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: a solicitação CORS não foi bem-sucedida). Saiba mais
  • Chromium baseado: o acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política cors: a resposta à solicitação de pré-vôo não passa o controle de acesso marcar: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação de autor, chame 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();

Os navegadores não são consistentes em como eles definem Access-Control-Request-Headers. Se um dos dois:

  • Cabeçalhos são definidos como qualquer outra coisa que não seja "*"
  • AllowAnyHeader é chamado: inclua pelo menos Accept, Content-Type e Origin, além de todos os cabeçalhos personalizados que você deseja dar suporte.

Código de solicitação de pré-vôo automático

Quando a política CORS é aplicada:

  • Globalmente chamando app.UseCors em Program.cs.
  • Usando o atributo [EnableCors].

ASP.NET Core responde à solicitação OPTIONS de pré-vôo.

Habilitar o CORS por ponto de extremidade usando RequireCors atualmente não dá suporte a solicitações de pré-voo automáticas.

A seção Testar CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para solicitações de pré-voo

Quando o CORS está habilitado com a política apropriada, ASP.NET Core geralmente responde às solicitações de pré-vôo CORS automaticamente. Em alguns cenários, esse pode não ser o caso. Por exemplo, usando CORS com roteamento de ponto de extremidade.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração do pré-vôo

O cabeçalho Access-Control-Max-Age especifica por quanto tempo a resposta à solicitação de pré-voo pode ser armazenada em cache. Para definir esse cabeçalho, chame 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();

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um recurso de segurança. O CORS é um padrão W3C que permite ao servidor flexibilizar a política de same-origin.
    • Por exemplo, um ator mal-intencionado pode usar o XSS (Script entre Sites) em seu site e executar uma solicitação entre sites no site habilitado para CORS para roubar informações.
  • Uma API não é mais segura permitindo o CORS.
    • Cabe ao cliente (navegador) impor o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem uma solicitação de API XHR ou Fetch de origem cruzada que, de outra forma, seria proibida.
    • Navegadores sem CORS não podem fazer solicitações entre origens. Antes do CORS, JSONP era usado para contornar essa restrição. O JSONP não usa XHR, ele usa a marca <script> para receber a resposta. Os scripts têm permissão para serem carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que habilitam solicitações entre origens. Se um navegador der suporte ao CORS, ele definirá esses cabeçalhos automaticamente para solicitações entre origens. O código JavaScript personalizado não é necessário para habilitar o CORS.

Veja a seguir um exemplo de uma solicitação entre origens do botão de teste Valores para https://cors1.azurewebsites.net/api/values. O cabeçalho Origin:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É necessário e deve ser diferente do host.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Em solicitações OPTIONS, o servidor define o cabeçalho Cabeçalhos de resposta Access-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, o exemplo implantado, a solicitação de botão Excluir [EnableCors]OPTIONS contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O valor https://cors1.azurewebsites.net desse cabeçalho corresponde ao cabeçalho Origin da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, será retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o cabeçalho Access-Control-Allow-Origin, a solicitação entre origens falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibiliza a resposta para o aplicativo cliente.

O redirecionamento HTTP para HTTPS causa ERR_INVALID_REDIRECT na solicitação de simulação do CORS

Solicitações para um ponto de extremidade usando HTTP que são redirecionadas para HTTPS por falha de UseHttpsRedirection com ERR_INVALID_REDIRECT on the CORS preflight request.

Os projetos de API podem rejeitar solicitações HTTP em vez de usar UseHttpsRedirection para redirecionar solicitações para HTTPS.

Exibir solicitações OPTIONS

Por padrão, os navegadores Chrome e Edge não mostram solicitações OPTIONS na guia de rede das ferramentas F12. Para exibir solicitações OPTIONS nestes navegadores:

  • chrome://flags/#out-of-blink-cors ou edge://flags/#out-of-blink-cors
  • desabilite o sinalizador.
  • reiniciar.

O Firefox mostra as solicitações OPTIONS por padrão.

CORS no IIS

Ao implantar no IIS, o CORS precisará ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir o acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Testar o CORS

O download de exemplo tem código para testar o CORS. Consulte como baixar. O exemplo é um projeto de API com Razor Pages (Páginas) adicionado:

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>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os pontos de extremidade para teste:

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

}

MyDisplayRouteInfo é fornecido pelo pacote NuGet Rick.Docs.Samples.RouteInfo e exibe as informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e examine os cabeçalhos na guia Rede.

  • Selecione o botão de teste PUT. Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações, uma solicitação de pré-vôo OPTIONS e a solicitação PUT.

  • Selecione o botão GetValues2 [DisableCors] para disparar uma solicitação CORS com falha. Conforme mencionado no documento, a resposta retorna 200 êxitos, mas a solicitação CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso para buscar na 'https://cors1.azurewebsites.net/api/values/GetValues2' origem 'https://cors3.azurewebsites.net' foi bloqueado pela política do CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Os pontos de extremidade habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo cabeçalho Origin deve ser diferente do host que recebe a solicitação. Se a solicitação não for entre origens com base no valor do cabeçalho Origin:

  • Não é necessário que o Middleware CORS processe a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir usa curl para emitir uma solicitação OPTIONS com informações:

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

Testar o CORS com o roteamento de ponto de extremidade e [HttpOptions]

Habilitar o CORS por ponto de extremidade usando RequireCors atualmente não dá suporte a solicitações de pré-voo automáticas. Considere o seguinte código que usa o roteamento de ponto de extremidade para habilitar o 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();

O seguinte TodoItems1Controller fornece pontos de extremidade para teste:

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

Teste o código anterior na página de teste (https://cors1.azurewebsites.net/test?number=1) da amostra implantada.

Os botões Excluir [EnableCors] e GET [EnableCors] têm êxito, pois os pontos de extremidade têm [EnableCors] e respondem a solicitações de simulação. Os outros pontos de extremidade falham. O botão GET falha porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece pontos de extremidade semelhantes, mas inclui código explícito para responder a solicitações 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);
}

O código anterior pode ser testado implantando o exemplo no Azure. Na lista suspensa Controlador, selecione Simulação e, em seguida, Definir Controlador. Todas as chamadas CORS para os pontos de extremidade TodoItems2Controller são bem-sucedidas.

Recursos adicionais

Por Rick Anderson e Kirk Larkin

Este artigo mostra como habilitar o CORS em um aplicativo ASP.NET Core.

A segurança do navegador impede que uma página da Web faça solicitações para um domínio diferente daquele que ofereceu a página da Web. Essa restrição é chamada de política de mesma origem. Essa restrição se chama política da mesma origem e impede que um site mal-intencional leia dados confidenciais de outro site. Às vezes, talvez você queira permitir que outros sites façam solicitações entre origens para seu aplicativo. Para obter mais informações, consulte o artigo do Mozilla CORS.

CORS (compartilhamento de recursos entre origens):

  • É um padrão W3C que permite que um servidor relaxe a política de mesma origem.
  • Não é um recurso de segurança, o CORS relaxa a segurança. Uma API não é mais segura permitindo CORS. Para saber mais, veja Como funciona CORS.
  • Permite que um servidor autorize explicitamente algumas solicitações de origem cruzada e rejeite outras.
  • É mais seguro e flexível do que técnicas anteriores, como JSONP.

Exibir ou baixar código de exemplo (como baixar)

Mesma origem

Duas URLs têm a mesma origem se tiverem esquemas, hosts e portas idênticos (RFC 6454).

Essas duas URLs têm a mesma origem:

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

Essas URLs têm origens diferentes das duas URLs anteriores:

  • https://example.net: Domínio diferente
  • https://www.example.com/foo.html: Subdomínio diferente
  • http://example.com/foo.html: Esquema diferente
  • https://example.com:9000/foo.html: Porta diferente

Habilitar CORS

Há três maneiras de habilitar CORS:

O uso do atributo [EnableCors] com uma política nomeada fornece o melhor controle na limitação de pontos de extremidade que dão suporte ao CORS.

Aviso

UseCors deve ser chamado na ordem correta. Para obter mais informações, veja ordem do Middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usando UseResponseCaching.

Cada abordagem é detalhada nas seções a seguir.

CORS com política nomeada e middleware

O Middleware CORS lida com solicitações entre origens. O código a seguir aplica uma política CORS a todos os pontos de extremidade do aplicativo com as origens especificadas:

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

O código anterior:

Com o roteamento de ponto de extremidade, o middleware CORS deve ser configurado para ser executado entre as chamadas para UseRouting e UseEndpoints.

Consulte Testar CORS para obter instruções sobre como testar código semelhante ao código anterior.

A chamada do método AddCors adiciona serviços CORS ao contêiner de serviços do aplicativo:

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 obter mais informações, consulte Opções de política CORS neste documento.

Os métodos CorsPolicyBuilder podem ser encadeados, conforme mostrado no código a seguir:

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

Observação: a URL especificada não deve conter uma barra à direita (/). Se a URL terminar com /, a comparação retornará false e nenhum cabeçalho será retornado.

CORS com política padrão e middleware

O código realçado a seguir habilita a política padrão do CORS:

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

O código anterior aplica a política CORS padrão a todos os pontos de extremidade do controlador.

Habilitar Cors com roteamento de ponto de extremidade

Habilitar o CORS por ponto de extremidade usando RequireCors não oferece suporte a solicitações de simulação automática. Para obter mais informações, confira esta edição do GitHub e Teste o CORS com roteamento de pontos de extremidade e [HttpOptions].

Com o roteamento de ponto de extremidade, o CORS pode ser habilitado por ponto de extremidade usando o RequireCors conjunto de métodos de extensão:

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

No código anterior:

  • app.UseCors adiciona o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() por si só não habilita o CORS.
  • Os pontos de extremidade do controlador e /echo permitem solicitações entre origens usando a política especificada.
  • Os pontos de extremidade /echo2 e Razor páginas não permitem solicitações entre origens porque nenhuma política padrão foi especificada.

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade com RequireCors.

Consulte Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar código semelhante ao anterior.

Habilitar CORS com atributos

Habilitar o CORS com o atributo [EnableCors] e aplicar uma política nomeada somente aos pontos de extremidade que exigem CORS fornece o melhor controle.

O atributo [EnableCors] fornece uma alternativa para aplicar o CORS globalmente. O atributo [EnableCors] habilita o CORS para pontos de extremidade selecionados, em vez de todos os pontos de extremidade:

  • [EnableCors] especifica a política padrão.
  • [EnableCors("{Policy String}")] especifica uma política nomeada.

O atributo [EnableCors] pode ser aplicado a:

  • Razor Página PageModel
  • Controller
  • Método de ação do controlador

Políticas diferentes podem ser aplicadas a controladores, modelos de página ou métodos de ação com o atributo [EnableCors]. Quando o atributo [EnableCors] é aplicado a um controlador, modelo de página ou método de ação e o CORS está habilitado no middleware, ambas as políticas são aplicadas. É recomendável não combinar políticas. Use o [EnableCors] atributo ou middleware, não ambos no mesmo aplicativo.

O código a seguir aplica uma política 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(),
        };
    }
}

O código a seguir cria duas 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 obter o melhor controle de limitação de solicitações CORS:

O código na próxima seção atende à lista anterior.

Consulte Testar CORS para obter instruções sobre como testar código semelhante ao código anterior.

Desabilitar CORS

O atributo [DisableCors]não desabilita o CORS que foi habilitado pelo roteamento de ponto de extremidade.

O código a seguir define a 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();
        });
    }
}

O código a seguir desabilita o CORS para a ação GetValues2:

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

}

O código anterior:

Consulte Testar CORS para obter instruções sobre como testar código semelhante ao código anterior.

Opções de política CORS

Esta seção descreve as várias opções que podem ser definidas em uma política CORS:

AddPolicy é chamado em Startup.ConfigureServices. Para algumas opções, pode ser útil ler primeiro a seção Como funciona o CORS.

Definir as origens permitidas

AllowAnyOrigin: permite solicitações CORS de todas as origens com qualquer esquema (http ou https). AllowAnyOrigin não é seguro porque qualquer site pode fazer solicitações entre origens para o aplicativo.

Observação

A especificação de AllowAnyOrigin e AllowCredentials não é uma configuração segura e pode resultar em uma solicitação intersite forjada. O serviço CORS retorna uma resposta CORS inválida quando um aplicativo é configurado com os dois métodos.

AllowAnyOrigin afeta as solicitações de pré-vôo e o Access-Control-Allow-Origin cabeçalho. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

SetIsOriginAllowedToAllowWildcardSubdomains: define a IsOriginAllowed propriedade da política como uma função que permite que as origens correspondam a um domínio curinga configurado ao avaliar se a origem é permitida.

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

Definir os métodos HTTP permitidos

AllowAnyMethod:

  • Permite qualquer método HTTP:
  • Afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Allow-Methods. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamado cabeçalhos de solicitação de autor, chame WithHeaders e especifique os cabeçalhos 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 os cabeçalhos de solicitação de autor, chame AllowAnyHeader:

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

AllowAnyHeader afeta as solicitações de pré-vôo e o cabeçalho Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações de pré-vôo.

Uma correspondência da política do CORS Middleware com cabeçalhos específicos especificados por WithHeaders só é possível quando os cabeçalhos enviados em Access-Control-Request-Headers correspondem exatamente aos cabeçalhos declarados em WithHeaders.

Por exemplo, considere um aplicativo configurado da seguinte maneira:

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

O MIDDLEware cors recusa uma solicitação de pré-vôo com o cabeçalho de solicitação a seguir porque Content-Language (HeaderNames.ContentLanguage) não está listado em WithHeaders:

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

O aplicativo retorna uma resposta 200 OK, mas não envia os cabeçalhos CORS de volta. Portanto, o navegador não tenta a solicitação entre origens.

Definir os cabeçalhos de resposta expostos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta ao aplicativo. Para obter mais informações, consulte Compartilhamento de recursos entre origens do W3C (Terminologia): cabeçalho de resposta simples.

Os cabeçalhos de resposta disponíveis por padrão são:

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

A especificação CORS chama esses cabeçalhos de cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, chame WithExposedHeaders:

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

Credenciais em solicitações entre origens

As credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia credenciais com uma solicitação de origem cruzada. As credenciais incluem cookies e esquemas de autenticação HTTP. Para enviar credenciais com uma solicitação entre origens, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando XMLHttpRequest diretamente:

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

Usando jQuery:

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

Usando a API Fetch:

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

O servidor deve permitir as credenciais. Para permitir credenciais entre origens, chame AllowCredentials:

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

A resposta HTTP inclui um Access-Control-Allow-Credentials cabeçalho, que informa ao navegador que o servidor permite credenciais para uma solicitação entre origens.

Se o navegador enviar credenciais, mas a resposta não incluir um cabeçalho válido Access-Control-Allow-Credentials, o navegador não exporá a resposta ao aplicativo e a solicitação entre origens falhará.

Permitir credenciais entre origens é um risco de segurança. Um site em outro domínio pode enviar as credenciais de um usuário conectado para o aplicativo em nome do usuário sem o conhecimento do usuário.

A especificação CORS também afirma que definir origens como "*" (todas as origens) é inválida se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Solicitações de pré-voo

Para algumas solicitações CORS, o navegador envia uma solicitação OPTIONS adicional antes de fazer a solicitação real. Essa solicitação é chamada de solicitação de pré-voo. O navegador poderá ignorar a solicitação de pré-vôo se todas as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST.
  • O aplicativo não define cabeçalhos de solicitação diferentes de Accept, Accept-Language, Content-Language, Content-Type ou Last-Event-ID.
  • O cabeçalho Content-Type, se definido, tem um dos seguintes valores:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

A regra nos cabeçalhos de solicitação definida para a solicitação do cliente se aplica aos cabeçalhos que o aplicativo define chamando setRequestHeader no objeto XMLHttpRequest. A especificação CORS chama esses cabeçalhos de cabeçalhos de solicitação de autor. A regra não se aplica a cabeçalhos que o navegador pode definir, como User-Agent, Host ou Content-Length.

Veja a seguir um exemplo de resposta semelhante à solicitação de pré-voo feita no botão [Colocar teste] na seção Testar CORS deste 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

A solicitação de pré-vôo usa o método HTTP OPTIONS. Ele pode incluir os seguintes cabeçalhos:

Se a solicitação de pré-voo for negada, o aplicativo retornará uma resposta 200 OK, mas não definirá os cabeçalhos CORS. Portanto, o navegador não tenta a solicitação entre origens. Para obter um exemplo de uma solicitação de pré-voo negada, consulte a seção Testar CORS deste documento.

Usando as ferramentas F12, o aplicativo de console mostra um erro semelhante a um dos seguintes, dependendo do navegador:

  • Firefox: solicitação entre origens bloqueada: a mesma política de origem não permite a leitura do recurso remoto em https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Motivo: a solicitação CORS não foi bem-sucedida). Saiba mais
  • Chromium baseado: o acesso para buscar em 'https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política cors: a resposta à solicitação de pré-vôo não passa o controle de acesso marcar: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Para permitir cabeçalhos específicos, chame 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 os cabeçalhos de solicitação de autor, chame AllowAnyHeader:

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

Os navegadores não são consistentes em como eles definem Access-Control-Request-Headers. Se um dos dois:

  • Cabeçalhos são definidos como qualquer outra coisa que não seja "*"
  • AllowAnyHeader é chamado: inclua pelo menos Accept, Content-Type e Origin, além de todos os cabeçalhos personalizados que você deseja dar suporte.

Código de solicitação de pré-vôo automático

Quando a política CORS é aplicada:

  • Globalmente chamando app.UseCors em Startup.Configure.
  • Usando o atributo [EnableCors].

ASP.NET Core responde à solicitação OPTIONS de pré-vôo.

Habilitar o CORS por ponto de extremidade usando RequireCors atualmente não dá suporte a solicitações de pré-voo automáticas.

A seção Testar CORS deste documento demonstra esse comportamento.

Atributo [HttpOptions] para solicitações de pré-voo

Quando o CORS está habilitado com a política apropriada, ASP.NET Core geralmente responde às solicitações de pré-vôo CORS automaticamente. Em alguns cenários, esse pode não ser o caso. Por exemplo, usando CORS com roteamento de ponto de extremidade.

O código a seguir usa o atributo [HttpOptions] para criar pontos de extremidade para solicitações 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 Testar CORS com roteamento de ponto de extremidade e [HttpOptions] para obter instruções sobre como testar o código anterior.

Definir o tempo de expiração do pré-vôo

O cabeçalho Access-Control-Max-Age especifica por quanto tempo a resposta à solicitação de pré-voo pode ser armazenada em cache. Para definir esse cabeçalho, chame SetPreflightMaxAge:

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

Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP.

  • O CORS não é um recurso de segurança. O CORS é um padrão W3C que permite ao servidor flexibilizar a política de same-origin.
    • Por exemplo, um ator mal-intencionado pode usar o XSS (Script entre Sites) em seu site e executar uma solicitação entre sites no site habilitado para CORS para roubar informações.
  • Uma API não é mais segura permitindo o CORS.
    • Cabe ao cliente (navegador) impor o CORS. O servidor executa a solicitação e retorna a resposta, é o cliente que retorna um erro e bloqueia a resposta. Por exemplo, qualquer uma das seguintes ferramentas exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem uma solicitação de API XHR ou Fetch de origem cruzada que, de outra forma, seria proibida.
    • Navegadores sem CORS não podem fazer solicitações entre origens. Antes do CORS, JSONP era usado para contornar essa restrição. O JSONP não usa XHR, ele usa a marca <script> para receber a resposta. Os scripts têm permissão para serem carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que habilitam solicitações entre origens. Se um navegador der suporte ao CORS, ele definirá esses cabeçalhos automaticamente para solicitações entre origens. O código JavaScript personalizado não é necessário para habilitar o CORS.

Veja a seguir um exemplo de uma solicitação entre origens do botão de teste Valores para https://cors1.azurewebsites.net/api/values. O cabeçalho Origin:

  • Fornece o domínio do site que está fazendo a solicitação.
  • É necessário e deve ser diferente do host.

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Em solicitações OPTIONS, o servidor define o cabeçalho Cabeçalhos de resposta Access-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, o exemplo implantado, a solicitação de botão Excluir [EnableCors]OPTIONS contém os seguintes cabeçalhos:

Cabeçalhos gerais

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

Cabeçalhos de resposta

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

Cabeçalhos de solicitação

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

Nos cabeçalhos de resposta anteriores, o servidor define o cabeçalho Access-Control-Allow-Origin na resposta. O valor https://cors1.azurewebsites.net desse cabeçalho corresponde ao cabeçalho Origin da solicitação.

Se AllowAnyOrigin for chamado, o Access-Control-Allow-Origin: *, o valor curinga, será retornado. AllowAnyOrigin permite qualquer origem.

Se a resposta não incluir o cabeçalho Access-Control-Allow-Origin, a solicitação entre origens falhará. Especificamente, o navegador não permite a solicitação. Mesmo que o servidor retorne uma resposta bem-sucedida, o navegador não disponibiliza a resposta para o aplicativo cliente.

Exibir solicitações OPTIONS

Por padrão, os navegadores Chrome e Edge não mostram solicitações OPTIONS na guia de rede das ferramentas F12. Para exibir solicitações OPTIONS nestes navegadores:

  • chrome://flags/#out-of-blink-cors ou edge://flags/#out-of-blink-cors
  • desabilite o sinalizador.
  • reiniciar.

O Firefox mostra as solicitações OPTIONS por padrão.

CORS no IIS

Ao implantar no IIS, o CORS precisará ser executado antes da Autenticação do Windows se o servidor não estiver configurado para permitir o acesso anônimo. Para dar suporte a esse cenário, o módulo CORS do IIS precisa ser instalado e configurado para o aplicativo.

Testar o CORS

O download de exemplo tem código para testar o CORS. Consulte como baixar. O exemplo é um projeto de API com Razor Pages (Páginas) adicionado:

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>"); só deve ser usado para testar um aplicativo de exemplo semelhante ao código de exemplo de download.

O seguinte ValuesController fornece os pontos de extremidade para teste:

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

}

MyDisplayRouteInfo é fornecido pelo pacote NuGet Rick.Docs.Samples.RouteInfo e exibe as informações de rota.

Teste o código de exemplo anterior usando uma das seguintes abordagens:

  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001.
  • Execute o exemplo do Visual Studio com a porta definida como 44398 para uma URL de https://localhost:44398.

Usando um navegador com as ferramentas F12:

  • Selecione o botão Valores e examine os cabeçalhos na guia Rede.

  • Selecione o botão de teste PUT. Consulte Exibir solicitações OPTIONS para obter instruções sobre como exibir a solicitação OPTIONS. O teste PUT cria duas solicitações, uma solicitação de pré-vôo OPTIONS e a solicitação PUT.

  • Selecione o botão GetValues2 [DisableCors] para disparar uma solicitação CORS com falha. Conforme mencionado no documento, a resposta retorna 200 êxitos, mas a solicitação CORS não é feita. Selecione a guia Console para ver o erro CORS. Dependendo do navegador, um erro semelhante ao seguinte é exibido:

    O acesso para buscar na 'https://cors1.azurewebsites.net/api/values/GetValues2' origem 'https://cors3.azurewebsites.net' foi bloqueado pela política do CORS: nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desabilitado.

Os pontos de extremidade habilitados para CORS podem ser testados com uma ferramenta, como curl ou Fiddler. Ao usar uma ferramenta, a origem da solicitação especificada pelo cabeçalho Origin deve ser diferente do host que recebe a solicitação. Se a solicitação não for entre origens com base no valor do cabeçalho Origin:

  • Não é necessário que o Middleware CORS processe a solicitação.
  • Os cabeçalhos CORS não são retornados na resposta.

O comando a seguir usa curl para emitir uma solicitação OPTIONS com informações:

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

Testar o CORS com o roteamento de ponto de extremidade e [HttpOptions]

Habilitar o CORS por ponto de extremidade usando RequireCors atualmente não dá suporte a solicitações de pré-voo automáticas. Considere o seguinte código que usa o roteamento de ponto de extremidade para habilitar o 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();
        });
    }
}

O seguinte TodoItems1Controller fornece pontos de extremidade para teste:

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

Teste o código anterior na página de teste (https://cors1.azurewebsites.net/test?number=1) da amostra implantada.

Os botões Excluir [EnableCors] e GET [EnableCors] têm êxito, pois os pontos de extremidade têm [EnableCors] e respondem a solicitações de simulação. Os outros pontos de extremidade falham. O botão GET falha porque o JavaScript envia:

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

O seguinte TodoItems2Controller fornece pontos de extremidade semelhantes, mas inclui código explícito para responder a solicitações 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);
}

O código anterior pode ser testado implantando o exemplo no Azure. Na lista suspensa Controlador, selecione Simulação e, em seguida, Definir Controlador. Todas as chamadas CORS para os pontos de extremidade TodoItems2Controller são bem-sucedidas.

Recursos adicionais