ASP.NET MVC & WebApi 中實現Cors來讓Ajax可以跨域訪問

風靈使發表於2018-07-10

什麼是Cors

CORS是一個W3C標準,全稱是”跨域資源共享”(Cross-origin resource sharing)。
它允許瀏覽器向跨源伺服器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
本文詳細介紹CORS的內部機制。

一、簡介

CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。

整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。

因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。

所以簡單來說Ajax不能夠跨域,完全是一個瀏覽器行為,其實Web伺服器程式(比如ASP.NETPHP等)在預設情況下是無法辨別也不會去管到來的一個Http請求是不是一個跨域的Ajax請求,所謂的Ajax請求無法跨域完全是一個瀏覽器機制,是瀏覽器阻止了Ajax的跨域請求。而CORS正是用來解決這個問題的,W3C定製CORS標準給予了瀏覽器一種機制來允許Ajax的跨域請求。

二、兩種請求

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

只要同時滿足以下兩大條件,就屬於簡單請求。

(1) 請求方法是以下三種方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的頭資訊不超出以下幾種欄位:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同時滿足上面兩個條件,就屬於非簡單請求。

瀏覽器對這兩種請求的處理,是不一樣的。

三、簡單請求

3.1 基本流程

對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭資訊之中,增加一個Origin欄位。

下面是一個例子,瀏覽器發現這次跨源AJAX請求是簡單請求,就自動在頭資訊之中,新增一個Origin欄位。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的頭資訊中,Origin欄位用來說明,本次請求來自哪個源(協議 + 域名 + 埠)。伺服器根據這個值,決定是否同意這次請求。

如果Origin指定的源,不在許可範圍內,伺服器會返回一個正常的HTTP迴應。瀏覽器發現,這個迴應的頭資訊沒有包含Access-Control-Allow-Origin欄位(詳見下文),就知道出錯了,從而丟擲一個錯誤,被XMLHttpRequestonerror回撥函式捕獲。注意,這種錯誤無法通過狀態碼識別,因為HTTP迴應的狀態碼有可能是200

如果Origin指定的域名在許可範圍內,伺服器返回的響應,會多出幾個頭資訊欄位。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的頭資訊之中,有三個與CORS請求相關的欄位,都以Access-Control-開頭。

(1)Access-Control-Allow-Origin

該欄位是必須的。它的值要麼是請求時Origin欄位的值,要麼是一個*,表示接受任意域名的請求。

(2)Access-Control-Allow-Credentials

該欄位可選。它的值是一個布林值,表示是否允許傳送Cookie。預設情況下,Cookie不包括在CORS請求之中。設為true,即表示伺服器明確許可,Cookie可以包含在請求中,一起發給伺服器。這個值也只能設為true,如果伺服器不要瀏覽器傳送Cookie,刪除該欄位即可。

(3)Access-Control-Expose-Headers

該欄位可選。CORS請求時,XMLHttpRequest物件的getResponseHeader()方法只能拿到6個基本欄位:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他欄位,就必須在Access-Control-Expose-Headers裡面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar欄位的值。

3.2 withCredentials 屬性

上面說到,CORS請求預設不傳送CookieHTTP認證資訊。如果要把Cookie發到伺服器,一方面要伺服器同意,指定Access-Control-Allow-Credentials欄位。

Access-Control-Allow-Credentials: true

另一方面,開發者必須在AJAX請求中開啟withCredentials屬性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否則,即使伺服器同意傳送Cookie,瀏覽器也不會傳送。或者,伺服器要求設定Cookie,瀏覽器也不會處理。

但是,如果省略withCredentials設定,有的瀏覽器還是會一起傳送Cookie。這時,可以顯式關閉withCredentials

xhr.withCredentials = false;

需要注意的是,如果要傳送CookieAccess-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用伺服器域名設定的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁程式碼中的document.cookie也無法讀取伺服器域名下的Cookie

四、非簡單請求

4.1 預檢請求

非簡單請求是那種對伺服器有特殊要求的請求,比如請求方法是PUTDELETE,或者Content-Type欄位的型別是application/json

非簡單請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為”預檢”請求(preflight)。

瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。

下面是一段瀏覽器的JavaScript指令碼。

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面程式碼中,HTTP請求的方法是PUT,並且傳送一個自定義頭資訊X-Custom-Header

瀏覽器發現,這是一個非簡單請求,就自動發出一個”預檢”請求,要求伺服器確認可以這樣請求。下面是這個”預檢”請求的HTTP頭資訊。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

“預檢”請求用的請求方法是OPTIONS(請注意:這就是為什麼ASP.NET MVCWebApi的路由無法解析CORS預檢請求到ControllerAction的原因,因為ASP.NET MVCWebApiRoute只會對常規的Http方法:Get,Put,Delete,Post請求生效,而OPTIONS方法的預檢請求並不會被ASP.NET MVCWebApi的路由處理),表示這個請求是用來詢問的。頭資訊裡面,關鍵欄位是Origin,表示請求來自哪個源。

除了Origin欄位,”預檢”請求的頭資訊包括兩個特殊欄位。

(1)Access-Control-Request-Method

該欄位是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT

(2)Access-Control-Request-Headers

該欄位是一個逗號分隔的字串,指定瀏覽器CORS請求會額外傳送的頭資訊欄位,上例是X-Custom-Header

4.2 預檢請求的迴應

伺服器收到”預檢”請求以後,檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers欄位以後,確認允許跨源請求,就可以做出迴應。

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

上面的HTTP迴應中,關鍵的是Access-Control-Allow-Origin欄位,表示http://api.bob.com可以請求資料。該欄位也可以設為星號,表示同意任意跨源請求。

Access-Control-Allow-Origin: *

如果瀏覽器否定了”預檢”請求,會返回一個正常的HTTP迴應,但是沒有任何CORS相關的頭資訊欄位。這時,瀏覽器就會認定,伺服器不同意預檢請求,因此觸發一個錯誤,被XMLHttpRequest物件的onerror回撥函式捕獲。控制檯會列印出如下的報錯資訊。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

伺服器迴應的其他CORS相關欄位如下。

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

(1)Access-Control-Allow-Methods

該欄位必需,它的值是逗號分隔的一個字串,表明伺服器支援的所有跨域請求的方法。注意,返回的是所有支援的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次”預檢”請求。

(2)Access-Control-Allow-Headers

如果瀏覽器請求包括Access-Control-Request-Headers欄位,則Access-Control-Allow-Headers欄位是必需的。它也是一個逗號分隔的字串,表明伺服器支援的所有頭資訊欄位,不限於瀏覽器在”預檢”中請求的欄位。

(3)Access-Control-Allow-Credentials

該欄位與簡單請求時的含義相同。

(4)Access-Control-Max-Age

該欄位可選,用來指定本次預檢請求的有效期,單位為秒。上面結果中,有效期是20天(1728000秒),即允許快取該條迴應1728000秒(即20天),在此期間,不用發出另一條預檢請求。

4.3 瀏覽器的正常請求和迴應

一旦伺服器通過了”預檢”請求,以後每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭資訊欄位。伺服器的迴應,也都會有一個Access-Control-Allow-Origin頭資訊欄位。

下面是”預檢”請求之後,瀏覽器的正常CORS請求。

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面頭資訊的Origin欄位是瀏覽器自動新增的。

下面是伺服器正常的迴應。

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8

上面頭資訊中,Access-Control-Allow-Origin欄位是每次迴應都必定包含的。

五、與JSONP的比較

CORSJSONP的使用目的相同,但是比JSONP更強大。

JSONP只支援GET請求,CORS支援所有型別的HTTP請求。JSONP的優勢在於支援老式瀏覽器,以及可以向不支援CORS的網站請求資料。


如何在ASP.NET MVCWebApi中響應Cors的請求?

一、ActionFilter OR HttpMessageHandler

通過上面針對W3CCORS規範的介紹,我們知道跨域資源共享實現的途徑就是資源的提供者利用預定義的響應報頭表明自己是否將提供的資源授權給了客戶端JavaScript程式,而支援CORS的瀏覽器利用這些響應報頭決定是否允許JavaScript程式操作返回的資源。對於ASP .NET Web API來說,如果我們具有一種機制能夠根據預定義的資源授權規則自動生成和新增針對CORS的響應報頭,那麼資源的跨域共享就迎刃而解了。

那麼如何利用ASP.NET Web API的擴充套件實現針對CORS響應報頭的自動新增呢?可能有人首先想到的是利用HttpActionFilter在目標Action方法執行之後自動新增CORS響應報頭。這種解決方案對於簡單跨域資源請求是沒有問題的,但是不要忘了:對於非簡單跨域資源請求,瀏覽器會採用“預檢(Preflight)”機制。目標Action方法只會在處理真正跨域資源請求的過程中才會執行,但是對於採用“OPTIONS”作為HTTP方法的預檢請求,根本找不到匹配的目標Action方法。

為了能夠有效地應付瀏覽器採用的預檢機制,我們只能在ASP.NET Web API的訊息處理管道級別實現對提供資源的授權檢驗和對CORS響應報頭的新增。我們只需要為此建立一個自定義的HttpMessageHandler即可,不過在此之前我們先來介紹用於定義資源授權策略的CorsAttribute特性。

二、使用ActionFilter實現簡單跨域請求的處理

基於ActionFilter的簡單跨域訪問設定,定義了一個ActionAllowOriginAttribute,繼承於ActionFilterAttribute,程式碼如下:

 public class ActionAllowOriginAttribute : ActionFilterAttribute
 {
        public string[] AllowSites { get; set; }
        public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
        {
            AllowOriginAttribute.onExcute(filterContext, AllowSites);
            base.OnActionExecuting(filterContext);
        }
 }

核心程式碼其實很簡單,就這麼幾行,主要生成CORS響應中規定的Http Header : Access-Control-Allow-Origin

public class AllowOriginAttribute
{
    public static void onExcute(ControllerContext context, string[] AllowSites)
    {
        var origin = context.HttpContext.Request.Headers["Origin"];
        Action action = () =>
        {
            context.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", origin);

        };
        if (AllowSites != null && AllowSites.Any())
        {
            if (AllowSites.Contains(origin))
            {
                action();
            }
        }

    }
}

二、使用自定義的HttpMessageHandler實現非簡單跨域請求的處理

CORS授權檢驗

在介紹自定義HttpMessageHandler之前,我們先看一下微軟官方定義的Asp.Net WebApi CORS處理類System.Web.Http.Cors.CorsMessageHandler的處理流程:
這裡寫圖片描述
實現在System.Web.Http.Cors.CorsMessageHandler中的具體CORS授權檢驗流程基本上體現在上圖中。它首先根據表示當前請求的HttpRequestMessage物件建立CorsRequestContext物件。然後利用註冊的CorsProviderFactory得到對應的CorsProvider物件,並利用後者得到針對當前請求的資源授權策略,這是一個CorsPolicy物件。

接下來,CorsMessageHandler會獲取註冊的CorsEngine。此前得到的CorsRequestContextCorsPolicy物件會作為引數呼叫CorsEngineEvaluatePolicy方法,CORS資源授權檢驗由此開始。授權檢驗結束之後,CorsMessageHandler會得到表示檢驗結果的CorsResult物件。

對於預檢請求,CorsMessageHandler會直接建立HttpResponseMessage物件予以響應。具體來說,如果預檢請求通過了授權檢驗,一個狀態為“200, OK”的HttpResponseMessage會被建立出來,通過CorsResult得到CORS響應報頭會被新增到這個HttpResponseMessage物件的報頭集合中。如果授權檢驗失敗,建立的HttpResponseMessage具有的狀態為“400, Bad Request”,CorsResult攜帶的錯誤響應會作為響應的主體內容。

對於非預檢請求,它會將當前請求傳遞給訊息處理管道的後續部分進行進一步處理,並最終得到表示響應訊息的HttpResponseMessage。只有在請求通過授權檢查的情況下,由CorsResult得到的CORS響應報頭才會被新增到此HttpResponseMessage的報頭集合中。

例項演示:建立MyCorsMessageHandler模擬具體採用的授權檢驗

針對簡單和非簡單跨域資源共享的實現最終體現在具有如下定義的MyCorsMessageHandler型別上,它直接繼承自DelegatingHandler,用於模擬System.Web.Http.Cors.CorsMessageHandler的實現機制。


public class MyCorsMessageHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        //根據當前請求建立CorsRequestContext
        CorsRequestContext context = request.CreateCorsRequestContext();

        //針對非預檢請求:將請求傳遞給訊息處理管道後續部分繼續處理,並得到響應
        HttpResponseMessage response = null;
        if (!context.IsPreflight)
        {
            response = await base.SendAsync(request, cancellationToken);
        }

        //利用註冊的CorsPolicyProviderFactory得到對應的CorsPolicyProvider
        //藉助於CorsPolicyProvider得到表示CORS資源授權策略的CorsPolicy
        HttpConfiguration configuration = request.GetConfiguration();
        CorsPolicy policy = await configuration.GetCorsPolicyProviderFactory().GetCorsPolicyProvider(request)
            .GetCorsPolicyAsync(request, cancellationToken);

        //獲取註冊的CorsEngine
        //利用CorsEngine對請求實施CORS資源授權檢驗,並得到表示檢驗結果的CorsResult物件
        ICorsEngine engine = configuration.GetCorsEngine();
        CorsResult result = engine.EvaluatePolicy(context, policy);

        //針對預檢請求
        //如果請求通過授權檢驗,返回一個狀態為“200, OK”的響應並新增CORS報頭
        //如果授權檢驗失敗,返回一個狀態為“400, Bad Request”的響應並指定授權失敗原因
        if (context.IsPreflight)
        {
            if (result.IsValid)
            {
                response = new HttpResponseMessage(HttpStatusCode.OK);
                response.AddCorsHeaders(result);
            }
            else
            {
                response = request.CreateErrorResponse(HttpStatusCode.BadRequest,
                    string.Join(" |", result.ErrorMessages.ToArray()));
            }
        }
        //針對非預檢請求
        //CORS報頭只有在通過授權檢驗情況下才會被新增到響應報頭集合中
        else if (result.IsValid)
        {
            response.AddCorsHeaders(result);
        }
        return response;
    }
}

如上面的程式碼片斷所示,我們首選在實現的SendAsync方法中呼叫自定義的擴充套件方法CreateCorsRequestContext根據表示當前請求的HttpRequestMessge物件建立出表示針對CORS的跨域資源請求上下文的CorsRequestContext物件。

然後我們根據CorsRequestContextIsPreflight屬性判斷當前是否是一個預檢請求。對於預檢請求,我們會直接呼叫基類的同名方法將請求傳遞給訊息處理管道的後續環節作進一步處理,並最終得到表示響應的HttpResponse物件。如下是CorsRequestContext的IsPreflight屬性的實現程式碼,主要就是根據判斷Http請求中是否有OriginAccess-Control-Request-Method兩個Http Header,以及Http方法是否是OPTIONS

public class CorsRequestContext
{
    //CorsRequestContext的其它程式碼....

    public bool IsPreflight
    {
        get
        {
            var request = HttpContext.Current.Request;

            return request.HttpMethod == HttpMethod.Options.ToString() &&
                   request.Headers.GetValues("Origin").Any() &&
                   request.Headers.GetValues("Access-Control-Request-Method").Any();
        }
    }
}

我們接下來從表示當前請求的HttpRequestMessge物件中直接獲取當前HttpConfiguration物件,並呼叫擴充套件方法GetCorsPolicyProviderFactory得到註冊在它上面的CorsPolicyProviderFactory,進而得到由它提供的GetCorsPolicyProvider。通過呼叫此GetCorsPolicyProvider的方法GetCorsPolicyAsync,我們會得到目標Action方法採用的CORS資源授權策略,這是一個CorsPolicy物件。

在這之後,我們呼叫HttpConfiguration物件的另一個擴充套件方法GetCorsEngine得到註冊其上的CorsEngine,並將此前得到的CorsRequestContextCorsPolicy物件作為引數呼叫它的方法EvaluatePolicy由此開始針對當前請求的CORS資源授權檢驗,並最終得到表示檢驗結果的CorsResult

通過CorsResultIsValid屬性表示當前請求是否通過CORS資源授權檢驗。對於預檢請求,在請求通過授權檢驗的情況下,我們會建立一個狀態為“200, OK”的HttpResponseMessage作為最終的響應,在返回之前我們呼叫自定義的擴充套件方法AddCorsHeaders將從CorsResult得到的CORS響應報頭新增到此HttpResponseMessage的報頭集合中。如果請求沒有通過授權檢驗,我們會返回一個狀態為“400, Bad Request”的響應,通過CorsResultErrorMessage屬性提取的錯誤訊息(表示授權失敗的原因)會作為響應的主體內容。

對於非預檢請求來說,只有在它通過了資源授權檢驗的情況下,我們才會呼叫擴充套件方法AddCorsHeaders將從CorsResult得到的CORS報頭新增響應的報頭集合中。換句話說,對於未取得授權的非預檢跨域資源請求,MyCorsMessageHandler沒有對響應作任何的改變。

如下所示的是分別針對HttpRequestMessageHttpResponseMessage定義的兩個擴充套件方法,其中CreateCorsRequestContext方法根據HttpRequestMessage建立CorsRequestContext物件,而AddCorsHeaders方法則將從CorsResult中獲取的CORS響應報頭新增到指定的HttpResponseMessage中。

public static class CorsExtensions
{
    public static CorsRequestContext CreateCorsRequestContext(this HttpRequestMessage request)
    {
        CorsRequestContext context = new CorsRequestContext
        {
            RequestUri = request.RequestUri,
            HttpMethod = request.Method.Method,
            Host = request.Headers.Host,
            Origin = request.GetHeader("Origin"),
            AccessControlRequestMethod = request.GetHeader("Access-Control-Request-Method")
        };

        string requestHeaders = request.GetHeader("Access-Control-Request-Headers");
        if (!string.IsNullOrEmpty(requestHeaders))
        {
            Array.ForEach(requestHeaders.Split(','), header => context.AccessControlRequestHeaders.Add(header.Trim()));
        }
        return context;
    }

    public static void AddCorsHeaders(this HttpResponseMessage response, CorsResult result)
    {
        foreach (var item in result.ToResponseHeaders())
        {
            response.Headers.TryAddWithoutValidation(item.Key, item.Value);
        }
    }

    private static string GetHeader(this HttpRequestMessage request, string name)
    {
        IEnumerable<string> headerValues;
        if (request.Headers.TryGetValues(name, out headerValues))
        {
            return headerValues.FirstOrDefault();
        }
        return null;
    }
}

為了驗證我們這個用於模擬CorsMessageHandler的自定義HttpMessageHandler是否能夠真正為ASP.NET Web API提供針對CORS的支援,我們通過上面介紹的方式為WebApi應用安裝“Microsoft ASP.NET Web API 2 Cross-Origin Support”這個NuGet包後,將EnableCorsAttribute特性應用到定義在ContactsController上並作如下的設定。

[EnableCors("http://localhost:9527", "*", "*")]
public class ContactsController : ApiController
{
    public IHttpActionResult GetAllContacts()
    {
        //省略實現
    }
}

Global.asax中,我們並不呼叫當前HttpConfigurationEnableCors方法開啟ASP.NET Web API針對CORS的支援,而是採用如下的方式將建立的CorsMessageHandler物件新增到訊息處理管道中。如果現在執行ASP.NET MVC程式,通過呼叫Web API以跨域Ajax請求得到的聯絡人列表依然會顯示在瀏覽器上。

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configuration.MessageHandlers.Add(new MyCorsMessageHandler());
        //其他操作
    }
}

HttpConfigurationEnableCors方法

通過上面的介紹我們知道針對ASP.NET Web APICORS程式設計首先需要做的就是在程式啟動之前呼叫當前HttpConfiguration的擴充套件方法EnableCors開啟對CORS的支援,那麼該方法中具體實現了怎樣操作呢?由於ASP.NET Web API針對CORS的支援最終是通過CorsMesssageHandler這個自定義的HttpMessageHandler來實現的,所以對於HttpConfiguration的擴充套件方法EnableCors來說,其核心操作就是對CorsMesssageHandler予以註冊。

public static class CorsHttpConfigurationExtensions
{
    public static void EnableCors(this HttpConfiguration httpConfiguration);
    public static void EnableCors(this HttpConfiguration httpConfiguration, ICorsPolicyProvider defaultPolicyProvider);
}

public class AttributeBasedPolicyProviderFactory : ICorsPolicyProviderFactory
{
    //其他成員
    public ICorsPolicyProvider DefaultPolicyProvider { get; set; }
}

如上面的程式碼片斷所示,HttpConfiguration具有兩個過載的EnableCors方法。其中一個可以指定一個預設的CorsPolicyProvider,如果呼叫此方法並指定一個具體的CorsPolicyProvider物件,一個AttributeBasedPolicyProviderFactory物件會被建立出來並註冊到HttpConfiguration上。而指定的CorsPolicyProvider實際上會作為AttributeBasedPolicyProviderFactory物件的DefaultPolicyProvider屬性。

如果想了解微軟官方NuGet包Microsoft.AspNet.WebApi.Cors的介紹和擴充套件可以檢視這個連結的文章

相關文章