呼叫 API 發生 CORS 錯誤的原因和 ASP.NET Core 6 的解決方式

假設使用 ASP.NET Core 6 建立了一個 API , 連結為 http://localhost:8080/user ,使用 Post 方法,需要在 Body 中帶入資料,在 Postman 中可以正常呼叫和取得回應,但是在別的網站上無法呼叫,在瀏覽器的開發人員工具的主控台中出現了下面的錯誤:

Firefox 錯誤訊息:
    
已封鎖跨來源請求: 同源政策不允許讀取 http://localhost:8080/user 的遠端資源。(原因: 缺少 CORS 'Access-Control-Allow-Origin' 檔頭)。狀態代碼: 302。

已封鎖跨來源請求: 同源政策不允許讀取 http://localhost:8080/user 的遠端資源。(原因: CORS 請求未成功)。狀態代碼: (null)。
    

Chrome 錯誤訊息:
    
POSThttp://localhost:8080/user net::ERR_FAILED

Access to XMLHttpRequest at 'http://localhost:8080/user' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
    

CORS 是什麼?

依據 mdn 上的說法, CORS(Cross-Origin Resource Sharing) 是跨來源資源共用,只要網域(domain)、通訊協定(protocol)或通訊埠(port) 不同時,就會產生跨來源 HTTP 請求(cross-origin HTTP request)。

CORS 是一個 Web 標準,用來限制瀏覽器從不同來源請求資源,只要瀏覽器檢查到不同來源的資源請求,就會進行檢查,確保使用者的資料安全。若該請求不為簡單請求(simple requests),則會觸發 CORS 預檢,發送一個 OPTIONS 請求,稱為預檢請求(Preflight Request),驗證伺服器回應的訊息中是否包含 Access-Control-Allow-Origin (所以瀏覽器中才會有兩個錯誤),由此確認伺服器是否允許本網頁跨來源請求資源。如果預檢請求沒有通過,確認目標伺服器拒絕本網頁跨來源請求資源,則不會發送原本請求。

而 Postman 不是瀏覽器,不受 CORS 策略的限制,所以使用 Postman 沒有 CORS 問題,不代表就沒有 CORS 問題,因為這個問題是瀏覽器為了安全而限制的。

所以通常遇到 CORS 問題時,不是呼叫 API 的客戶端(如網頁)的問題,而是伺服器端的問題(如 ASP.NET Core),前端工程師可以鬆一口氣,直接把問題甩到後端工程師身上(誤)。

ASP.NET Core 6 解決 CORS 的方式

在 var app = builder.Build(); 前加入下面的程式碼:
    
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(builder =>
    {
        builder
        	.AllowAnyOrigin() // 允許任何來源
            .AllowAnyMethod()
            .AllowAnyHeader();
    });
});
    

或是要設定只能允許幾個來源,可以將第六行替換,就可以以白名單的形式設定
    
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(builder =>
    {
        builder
        	.WithOrigins("http://localhost:5173", "https://localhost:44300") // 只允許這兩個來源
            .AllowAnyMethod()
            .AllowAnyHeader();
    });
});
    

最後在 UseRouting 後面和 UseEndpoints 前面加入 UseCors 即可,程式碼如下:
    
var app = builder.Build();
app.UseRouting();

app.UseCors();

app.UseEndpoints();
    

若沒有 UseEndpoints 則加在 builder.Build() 的後方就可以了。

再次重新發送請求時就不會再出現 CORS 錯誤了!

參考資料:
mdn.docs - 跨來源資源共用(CORS)
StackOverflow - Access Control Origin Header error using Axios
StackOverflow - POSTing to external API throws CORS but it works from Postman
Microsoft.Learn - Enable Cross-Origin Requests (CORS) in ASP.NET Core

留言