Skip to content
Rain Hu's Workspace
Go back

[IT] CORS 跨原始來源要求

Rain Hu

TL;DR

CORS (Cross-Origin Resource Sharing)
是一個支援安全跨源請求和資料傳輸的機制,用於在瀏覽器和伺服器之間進行跨源請求。

何謂相同原始來源

如何啟動 CORS

有三種方式可以啟用 CORS:

⚠️ Warning

必須以正確的順序呼叫 UseCors。例如,在使用 UseResponseCaching 時,必須先呼叫 UseCors,再呼叫 UseResponseCaching

具有具名原則和中介軟體的 CORS

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");
                          // policy.WithOrigins("http://example.com", "http://www.contoso.com")
                          //       .AllowAnyHeader()
                          //       .AllowAnyMethod();       ### policyBuilder 可以鏈結 ###
                      });
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();

上述 程式碼:

使用端點路由時,CORS 中介軟體必須設定為在呼叫 UseRoutingUseEndpoints 之間執行。
AddCors 方法呼叫會將 CORS 服務新增至應用程式的服務容器:

⚠️ Warning

注意:指定的 URL 不得包含尾端斜線 (/)。 如果 URL 以 / 終止,則比較會傳回 false,而且不會傳回任何標頭。


具有預設原則和中介軟體的 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();

上述程式碼會將預設 CORS 原則套用至所有控制器端點。


使用端點路由啟用 CORS

使用端點路由時,可以使用一組 RequiredCors 擴充方法,逐個端點啟用 CORS:

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

在上述程式碼中:


使用屬性啟用 CORS

使用[EnableCors]屬性啟用 CORS,並將具名原則套用至只需要 CORS 的端點,這樣才能提供最精細的控制。 [EnableCors] 屬性提供全域套用 CORS 的替代方案。[EnableCors] 屬性會啟用所選端點的 CORS,而不是所有端點:

[EnableCors] 屬性可以套用至:

下列程式碼會將不同的原則套用至每個方法:

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

下列程式碼會建立兩個 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();

如需對 CORS 要求限制進行最精細的控制:

搭配具名原則使用 [EnableCors("MyPolicy")]

停用 CORS

如果 CORS 是由端點路由所啟用的,則 [DisableCors] 屬性無法停用 CORS。

下列程式碼會定義 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();

下列程式碼會停用 GetValues2 動作的 CORS:

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

上述程式碼:


Share this post on:

Previous
[Hugo] 使用 Hugo-notice
Next
[ML] General guide on ML