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

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 servia a página da Web. Essa restrição é chamada de política de mesma origem. A política de mesma origem impede que um site mal-intencionado 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 de CORS do Mozilla.

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 libera a segurança. Uma API não é mais segura, permitindo CORS. Para obter mais informações, consulte como o CORS funciona.
  • Permite que um servidor permita explicitamente algumas solicitações entre origens enquanto rejeita outras.
  • É mais seguro e flexível do que as 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 o 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 a CORS.

Aviso

UseCors deve ser chamado na ordem correta. Para obter mais informações, consulte ordem de middleware. Por exemplo, UseCors deve ser chamado antes de UseResponseCaching usar 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,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

// services.AddResponseCaching();

builder.Services.AddControllers();

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

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

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 o código de teste semelhante ao código anterior.

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

var  MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

// services.AddResponseCaching();

builder.Services.AddControllers();

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

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Para obter mais informações, consulte Opções de política de CORS neste documento.

Os CorsPolicyBuilder métodos 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,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
});

builder.Services.AddControllers();

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

app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();

app.Run();

Observação: a URL especificada não deve conter uma barra à direita ( / ). Se a URL for encerrada 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 CORS padrão:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddControllers();

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

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 em uma base por ponto de extremidade usando RequireCors o não oferece suporte a solicitações de simulação automáticas. para obter mais informações, consulte este GitHub problema e testar o CORS com roteamento de ponto de extremidade e [httpoptions].

Com o roteamento de ponto de extremidade, o CORS pode ser habilitado em uma base 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,
                      builder =>
                      {
                          builder.WithOrigins("http://example.com",
                                              "http://www.contoso.com");
                      });
});

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

    endpoints.MapControllers()
             .RequireCors(MyAllowSpecificOrigins);

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

    endpoints.MapRazorPages();
});

app.Run();

No código anterior:

  • app.UseCors habilita o middleware CORS. Como uma política padrão não foi configurada, app.UseCors() sozinha não habilita o CORS.
  • Os /echo pontos de extremidade do controlador e permitem solicitações entre origens usando a política especificada.
  • Os /echo2 pontos de extremidade e de 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 roteamento de ponto de extremidade e [httpoptions] para obter instruções sobre o código de teste semelhante ao anterior.

Habilitar CORS com atributos

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

O atributo [EnableCors] fornece uma alternativa para aplicar o CORS globalmente. O [EnableCors] atributo 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 [EnableCors] atributo pode ser aplicado a:

  • Razor Web 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 [EnableCors] atributo. Quando o [EnableCors] atributo é aplicado a um controlador, modelo de página ou método de ação e o CORS é habilitado no middleware, ambas as políticas são aplicadas. Recomendamos a combinação de 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",
        builder =>
        {
            builder.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });

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

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();

Para o melhor controle da limitação de solicitações de CORS:

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

Consulte testar CORS para obter instruções sobre o código de teste 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" :

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

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

[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 o código anterior.

Opções de política de 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 a seção como o CORS funciona primeiro.

Definir as origens permitidas

AllowAnyOrigin: Permite solicitações CORS de todas as origens com qualquer esquema ( http ou https ). AllowAnyOrigin o 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 simulação e o Access-Control-Allow-Origin cabeçalho. Para obter mais informações, consulte a seção solicitações de simulaçã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,
        builder =>
        {
            builder.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 simulação e o Access-Control-Allow-Methods cabeçalho. Para obter mais informações, consulte a seção solicitações de simulação .

Definir os cabeçalhos de solicitação permitidos

Para permitir que cabeçalhos específicos sejam enviados em uma solicitação CORS, chamada criar 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,
       builder =>
       {
           builder.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,
        builder =>
        {
            builder.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

AllowAnyHeader afeta as solicitações de simulação e o cabeçalho Access-Control-request-headers . Para obter mais informações, consulte a seção solicitações de simulação .

Uma política de middleware CORS correspondente a cabeçalhos específicos especificados por WithHeaders é possível somente quando os cabeçalhos enviados Access-Control-Request-Headers exatamente correspondem aos cabeçalhos indicados 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 simulação com o seguinte cabeçalho de solicitação porque Content-Language (headernames. ContentLanguage) não está listado em WithHeaders :

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

O aplicativo retorna uma resposta de 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 que estão 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 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",
        builder =>
        {
            builder.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 entre origens. As credenciais incluem cookie s 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 o jQuery:

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

Usando a API de busca:

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",
        builder =>
        {
            builder.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 Access-Control-Allow-Credentials cabeçalho válido, 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 indica que a definição de origens para "*" (todas as origens) é inválida se o Access-Control-Allow-Credentials cabeçalho estiver presente.

Solicitações de simulação

Para algumas solicitações de CORS, o navegador envia uma solicitação de Opções adicional antes de fazer a solicitação real. Essa solicitação é chamada de uma solicitação de simulação. O navegador poderá ignorar a solicitação de simulaçã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 Content-Type cabeçalho, 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 definidos para a solicitação do cliente se aplica aos cabeçalhos que o aplicativo define chamando setRequestHeader no XMLHttpRequest objeto. A especificação CORS chama esses cabeçalhos de solicitação de autorde cabeçalho. A regra não se aplica aos 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 simulação feita no botão [Put Test] 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 simulação usa o método de Opções http . Ele pode incluir os seguintes cabeçalhos:

Se a solicitação de simulação for negada, o aplicativo retornará uma 200 OK resposta, 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 simulação 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 teve sucesso). Saiba mais
  • baseado em Chromium: o acesso à busca em ' https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 ' da origem ' https://cors3.azurewebsites.net ' foi bloqueado pela política de CORS: a resposta à solicitação de simulação não passou na verificação de controle de acesso: 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",
        builder =>
        {
        builder.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",
        builder =>
        {
            builder.WithOrigins("https://*.example.com")
                   .AllowAnyHeader();
        });
});

builder.Services.AddControllers();

var app = builder.Build();

Os navegadores não são consistentes em como eles são definidos Access-Control-Request-Headers . Se:

  • Os cabeçalhos são definidos como algo diferente de "*"
  • AllowAnyHeader é chamado: inclua pelo menos Accept , Content-Type e Origin , além de todos os cabeçalhos personalizados aos quais você deseja dar suporte.

Código de solicitação de simulação automática

Quando a política CORS for aplicada:

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

ASP.NET Core responde à solicitação de simulação de opções.

Habilitar o CORS em uma base por ponto de extremidade usando RequireCors atualmente não oferece suporte a solicitações de simulação automáticas.

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

[Httpoptions] atributo para solicitações de simulação

quando o CORS é habilitado com a política apropriada, ASP.NET Core geralmente responde às solicitações de simulação de 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);
    }

Confira 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é-voo

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

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddControllers();

var app = builder.Build();

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 que um servidor relaxe a política de mesma origem.
    • Por exemplo, um ator mal-intencionado pode usar XSS (Cross-Site Scripting) em seu site e executar uma solicitação entre sites para seu site habilitado para CORS para roubar informações.
  • Uma API não é mais segura permitindo o CORS.
    • O cliente (navegador) deve 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 ferramentas a seguir exibirá a resposta do servidor:
  • É uma maneira de um servidor permitir que os navegadores executem uma solicitação de API XHR ou Fetch entre origens que, de outra forma, seria proibido.
    • Navegadores sem CORS não podem fazer solicitações entre origens. Antes do CORS, o JSONP era usado para contornar essa restrição. JSONP não usa XHR, ele usa a <script> marca 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 permitem solicitações entre origens. Se um navegador dá suporte ao CORS, ele define esses headers automaticamente para solicitações entre origens. O código JavaScript personalizado não é necessário para habilitar o CORS.

O botão de teste PUT no exemplo implantado

A seguir está um exemplo de uma solicitação entre origens do botão de teste Valores para https://cors1.azurewebsites.net/api/values . O Origin header:

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

Headers 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 OPTIONS solicitações, o servidor define o header De resposta na Access-Control-Allow-Origin: {allowed origin} resposta. Por exemplo, a solicitação de botão de exemploimplantada , Excluir [EnableCors] OPTIONS contém os seguintes headers:

Headers 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 headers de Resposta anteriores, o servidor define o header Access-Control-Allow-Origin na resposta. O https://cors1.azurewebsites.net valor desse header corresponde ao Origin header da solicitação.

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

Se a resposta não incluir o Access-Control-Allow-Origin header, 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 nesses 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 precisa ser executado antes Windows autenticação 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 Páginas adicionadas:

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

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 Rick.Docs.Samples.RouteInfo NuGet e exibe informações de rota.

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

  • Use o aplicativo de exemplo implantado em https://cors3.azurewebsites.net/ . Não é necessário baixar o exemplo.
  • Execute o exemplo com dotnet run usando a URL padrão de https://localhost:5001 .
  • Execute o exemplo de 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 revise os headers 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 options preflight e a solicitação PUT.

  • Selecione o GetValues2 [DisableCors] botão 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 origem foi bloqueado pela política 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' do CORS: nenhum header '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, Fiddlerou Postman. Ao usar uma ferramenta, a origem da solicitação especificada pelo Origin header deve ser diferente do host que está recebendo a solicitação. Se a solicitação não for entre origens com base no valor do Origin header:

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

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

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

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

A habilitação do CORS por ponto de extremidade usando atualmente não dá suporte a solicitações RequireCors de pré-piloto automáticas. Considere o código a seguir, 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",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
});

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

var app = builder.Build();

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

app.UseCors();

app.UseAuthorization();

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

app.Run();

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 do exemplo implantado.

Os botões Excluir [EnableCors] e GET [EnableCors] são bem-sucedidos, pois os pontos de extremidade têm e respondem a solicitações [EnableCors] de pré-voo. 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);
}

Teste o código anterior na página de teste do exemplo implantado. Na lista do controlador listada, selecione Preflight e, em seguida, Definir Controlador. Todas as chamadas cors para os TodoItems2Controller pontos de extremidade são bem-sucedidas.

Recursos adicionais

Por Rick Anderson e Kirk Ltda

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

A segurança do navegador impede que uma página da Web efetue solicitações para um domínio diferente do que atendeu à página da Web. Essa restrição é chamada de política de mesma origem. A política de mesma origem impede que um site mal-intencionado 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 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 diminui a segurança. Uma API não é mais segura permitindo o CORS. Para obter mais informações, consulte Como o CORS funciona.
  • Permite que um servidor permita explicitamente algumas solicitações entre origens ao rejeitar outras.
  • É mais seguro e mais flexível do que as técnicas anteriores, como JSONP.

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

Mesma origem

Duas URLs terão a mesma origem se elas têm 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 o CORS:

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

Aviso

UseCors deve ser chamado na ordem correta. Para obter mais informações, consulte Ordem de middleware. Por exemplo, UseCors deve ser chamado antes de usar UseResponseCaching 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 de 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,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

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

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

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

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

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

O código anterior:

  • Define o nome da política como _myAllowSpecificOrigins . O nome da política é arbitrário.
  • Chama o UseCors método de extensão e especifica a política do _myAllowSpecificOrigins CORS. UseCors adiciona o middleware cors. A chamada a UseCors deve ser feita após , mas antes de UseRouting UseAuthorization . Para obter mais informações, consulte Ordem de middleware.
  • Chama AddCors com uma expressão lambda. O lambda recebe um CorsPolicyBuilder objeto . As opções deconfiguração, como WithOrigins , são descritas posteriormente neste artigo.
  • Habilita a _myAllowSpecificOrigins política cors para todos os pontos de extremidade do controlador. Consulte roteamento de ponto de extremidade para aplicar uma política de CORS a pontos de extremidade específicos.
  • Ao usar o Middleware Caching Resposta, chame antes de UseCors UseResponseCaching .

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

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

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

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

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

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

Para obter mais informações, consulte Opções de política do CORS neste documento.

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

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

    services.AddControllers();
}

Observação: a URL especificada não deve conter uma barra à parte final ( / ). Se a URL terminar com / , a comparação retornará e nenhum título false será retornado.

CORS com middleware e política padrão

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

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

        services.AddControllers();
    }

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

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

        app.UseCors();

        app.UseAuthorization();

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

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

A habilitação do CORS por ponto de extremidade usando não dá suporte a solicitações RequireCors de pré-piloto automáticas. Para obter mais informações, consulte este GitHub e Testar CORS com roteamento de ponto 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,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

            endpoints.MapControllers()
                     .RequireCors(MyAllowSpecificOrigins);

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

            endpoints.MapRazorPages();
        });
    }
}

No código anterior:

  • app.UseCors habilita o middleware cors. Como uma política padrão não foi configurada, app.UseCors() o cors não habilita o CORS sozinho.
  • Os /echo pontos de extremidade do controlador e permitem solicitações entre origens usando a política especificada.
  • Os /echo2 pontos de extremidade e Pages não Razor 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 [EnableCors] atributo 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 [EnableCors] atributo 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 [EnableCors] atributo . Quando o atributo é aplicado a um controlador, modelo de página ou método de ação e o CORS está habilitado no [EnableCors] middleware, ambas as políticas são aplicadas. Recomendamos a combinação de 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 de CORS:

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

    public IConfiguration Configuration { get; }

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

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

        services.AddControllers();
    }

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

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

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

Para o melhor controle de limitação de solicitações CORS:

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

Confira 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 "MyPolicy" cors:

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

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

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

[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:

Confira Testar CORS para obter instruções sobre como testar o código anterior.

Opções de política do CORS

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

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

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 solicitações de pré-voo e Access-Control-Allow-Origin o header. Para obter mais informações, consulte a seção Solicitações de pré-voo.

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

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

Definir os métodos HTTP permitidos

AllowAnyMethod:

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

Definir os headers de solicitação permitidos

Para permitir que os headers específicos sejam enviados em uma solicitação CORS, chamados de headersde solicitação de autor, chame e WithHeaders especifique os headers permitidos:

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

Para permitir que todos os headers de solicitação de autor, chame AllowAnyHeader :

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

AllowAnyHeaderafeta as solicitações de pré-voo e o header Access-Control-Request-Headers. Para obter mais informações, consulte a seção Solicitações de pré-voo.

Uma política de Middleware cors corresponder a headers específicos especificados por só é possível quando os headers enviados em corresponder exatamente aos WithHeaders Access-Control-Request-Headers headers declarados em WithHeaders .

Por exemplo, considere um aplicativo configurado da seguinte forma:

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

O Middleware CORS recusa uma solicitação de pré-voo com o seguinte header de solicitação 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 headers cors de volta. Portanto, o navegador não tenta a solicitação entre origens.

Definir os headers de resposta expostos

Por padrão, o navegador não expõe todos os headers de resposta para o aplicativo. Para obter mais informações, consulte W3C Cross-Origin Resource Sharing (Terminologia): Simple Response Header.

Os headers de resposta que estão disponíveis por padrão são:

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

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

options.AddPolicy("MyExposeResponseHeadersPolicy",
    builder =>
    {
        builder.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 entre origens. As credenciais incluem cookie esquemas de autenticação HTTP e s. 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",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowCredentials();
    });

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

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

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

A especificação cors também declara que definir origens como "*" (todas as origens) será inválido se Access-Control-Allow-Credentials o header estiver presente.

Solicitações de pré-voo

Para algumas solicitações de 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é-configuração se todas as seguintes condições são verdadeiras:

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

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

A seguir está um exemplo de resposta semelhante à solicitação de pré-teste feita do botão [Colocar teste] na seção CORS de teste 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é-configuração usa o método HTTP OPTIONS. Ele pode incluir os seguintes headers:

Se a solicitação de pré-voo for negada, o aplicativo retornará uma resposta, mas 200 OK não definirá os headers do CORS. Portanto, o navegador não tenta a solicitação entre origens. Para ver um exemplo de uma solicitação de pré-teste negada, consulte a seção CorS de teste 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: o acesso para buscar em ' ' da origem ' ' foi bloqueado pela política do CORS: a resposta à solicitação de pré-voo não passa na verificação de controle de acesso: nenhum https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net header '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 os headers específicos, chame WithHeaders :

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

Para permitir que todos os headers de solicitação de autor, chame AllowAnyHeader :

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

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

  • Os headers são definidos como qualquer coisa diferente de "*"
  • AllowAnyHeader é chamado: inclua pelo menos , e , além de todos os headers personalizados Accept que você deseja dar Content-Type Origin suporte.

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

Quando a política do CORS é aplicada:

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

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

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

A seção CorS de teste deste documento demonstra esse comportamento.

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

Quando o CORS está habilitado com a política apropriada, o ASP.NET Core geralmente responde às solicitações de pré-voo do 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);
    }

Confira 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é-voo

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

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

Como o CORS funciona

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

  • O CORS não é um recurso de segurança. O CORS é um padrão W3C que permite que um servidor Relaxe a política de mesma origem.
    • Por exemplo, um ator mal-intencionado pode usar XSS (script entre sites) em seu site e executar uma solicitação entre sites em seu site habilitado para CORS para roubar informações.
  • Uma API não é mais segura, permitindo 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 de XHR ou de busca de várias origens que, de outra forma, seriam proibidas.
    • Os navegadores sem CORS não podem fazer solicitações entre origens. Antes do CORS, JSONP foi usado para burlar essa restrição. JSONP não usa XHR, ele usa a <script> marca para receber a resposta. Os scripts podem ser carregados entre origens.

A especificação CORS introduziu vários novos cabeçalhos HTTP que habilitam solicitações entre origens. Se um navegador oferecer suporte a 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.

O botão Put Test no exemplo implantado

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

  • 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 OPTIONS solicitações, o servidor define o cabeçalho de cabeçalhos de resposta Access-Control-Allow-Origin: {allowed origin} na resposta. Por exemplo, a solicitação de botão de exemploimplantada, delete [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 https://cors1.azurewebsites.net valor desse cabeçalho corresponde ao Origin cabeçalho da solicitação.

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

Se a resposta não incluir o Access-Control-Allow-Origin cabeçalho, a solicitação entre origens falhará. Especificamente, o navegador não permite a solicitação. Mesmo se o servidor retornar uma resposta bem-sucedida, o navegador não tornará a resposta disponível para o aplicativo cliente.

Exibir solicitações de opções

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

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

O Firefox mostra solicitações de opções por padrão.

CORS no IIS

ao implantar no IIS, o CORS precisará ser executado antes de Windows autenticação se o servidor não estiver configurado para permitir 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 CORS. Consulte como baixar. O exemplo é um projeto de API com Razor páginas adicionadas:

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

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 por Rick. Docs. samples. RouteInfo NuGet pacote e exibe informações de rota.

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

  • Use o aplicativo de exemplo implantado em https://cors3.azurewebsites.net/ . Não é necessário baixar o exemplo.
  • Execute o exemplo com dotnet run o uso da URL padrão de https://localhost:5001 .
  • execute o exemplo de 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 Put Test . Consulte Exibir opções solicitações para obter instruções sobre como exibir a solicitação de opções. O teste Put cria duas solicitações, uma solicitação de simulação de opções e a solicitação Put.

  • Selecione o GetValues2 [DisableCors] botão para disparar uma solicitação de CORS com falha. Conforme mencionado no documento, a resposta retorna 200 êxito, 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 à busca 'https://cors1.azurewebsites.net/api/values/GetValues2' da origem 'https://cors3.azurewebsites.net' foi bloqueado pela política 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 ondulação, Fiddlerou postmaster. Ao usar uma ferramenta, a origem da solicitação especificada pelo Origin cabeçalho deve ser diferente do host que está recebendo a solicitação. Se a solicitação não for de origem cruzada com base no valor do Origin cabeçalho:

  • Não há necessidade de middleware de CORS para processar a solicitação.
  • Cabeçalhos CORS não são retornados na resposta.

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

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

Testar CORS com roteamento de ponto de extremidade e [Httpoptions]

Habilitar o CORS em uma base por ponto de extremidade usando RequireCors atualmente não oferece suporte a solicitações de simulação automáticas. Considere o código a seguir, que usa o Roteamento de ponto de extremidade para habilitar CORS:

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

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

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

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

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

        app.UseCors();

        app.UseAuthorization();

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

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

Os botões delete [EnableCors] e Get [EnableCors] são bem-sucedidos, 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 às solicitações de opções:

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

Teste o código anterior na página de teste do exemplo implantado. Na lista suspensa controlador , selecione simulação e, em seguida, definir controlador. Todas as chamadas de CORS para os TodoItems2Controller pontos de extremidade são bem-sucedidos.

Recursos adicionais