Web API 2 對 CORS 的支持
CORS概念
跨域資源共享 (CORS) 是一種萬維網聯合會 (W3C) 規范(通常被認為是 HTML5 的一部分),它可讓 JavaScript 克服由瀏覽器施加的同域策略安全限制。 所謂同域策略,就是 JavaScript 只能對包含網頁的同一個域進行 AJAX 回調(其中,“域”就是主機名、協議和端口號的組合)。 例如,http://foo.com 中某個網頁上的 JavaScript 無法對 http://bar.com(或 http://www.foo.com、https://foo.com 或 http://foo.com:999 等)進行 AJAX 調用。
CORS 可讓服務器指明允許哪些域對它們進行調用,從而放寬這種限制。 CORS 是由瀏覽器強制執行的,並且必須在服務器上實現,而最新版本的 ASP.NET Web API 2 全面支持 CORS。 通過 Web API 2,您可以對策略進行配置以允許不同域的 JavaScript 客戶端訪問您的 API。
CORS 基本信息
由於 Web API 完全按照該規范來實現,因此,為了使用 Web API 中的新 CORS 功能,詳細了解 CORS 本身將大有幫助。 這些詳細內容現在看起來可能都是理論之談,但對於以后了解 Web API 中的可用設置來說將十分有用:在您調試 CORS 時,這些內容有助於您更快速地解決問題。
CORS 的一般機制是,當 JavaScript 嘗試進行跨域 AJAX 調用時,瀏覽器會通過在 HTTP 請求中發送標頭(如“Origin”)來“詢問”服務器是否允許進行這樣的調用。 服務器通過在響應中返回 HTTP 標頭(如“Access-Control-Allow-Origin”)指明允許的操作。 這種權限檢查將針對客戶端調用的每個不同 URL 進行,這就意味着不同的 URL 可以具有不同權限。
除域之外,CORS 還可以讓服務器指明允許使用的 HTTP 方法、客戶端可以發送的 HTTP 請求標頭、客戶端可以讀取的 HTTP 響應標頭以及是否允許瀏覽器自動發送或接收憑據(Cookie 或授權標頭)。 其他請求和響應標頭指明允許使用其中的哪些功能。 圖 1 總結了這些標頭(請注意,一些功能沒有在響應中發送的標頭,僅有響應)。
圖 1 CORS HTTP 標頭
權限/功能 | 請求標頭 | 響應標頭 |
域 | 域 | Access-Control-Allow-Origin |
HTTP 方法 | Access-Control-Request-Method | Access-Control-Allow-Method |
請求標頭 | Access-Control-Request-Headers | Access-Control-Allow-Headers |
響應標頭 | Access-Control-Expose-Headers | |
憑據 | Access-Control-Allow-Credentials | |
緩存預檢響應 | Access-Control-Max-Age |
瀏覽器可通過兩種不同的方式向服務器請求這些權限:簡單 CORS 請求和預檢 CORS 請求。
Web API 2 對 CORS 的支持
Web API 中對 CORS 的支持是一個完整框架,允許應用程序定義 CORS 請求的權限。 該框架圍繞一個策略方案展開,該策略方案可讓您指定針對進入應用程序的任何給定請求而允許的 CORS 功能。
首先,為了獲取該 CORS 框架,您必須從 Web API 應用程序引用 CORS 庫(默認情況下,Visual Studio 2013 中的任何 Web API 模板都不引用這些庫)。 該 Web API CORS 框架通過 NuGet 作為 Microsoft.AspNet.WebApi.Cors 程序包提供。 早 nuget中輸入
Install-Package Microsoft.AspNet.WebApi.Cors
注意 Web api 2對,net framework的要求必須是4.5以上,安裝完上面的package后,會發現引用中多了兩個重要的包,如下圖所示
接下來,為了表達該策略,Web API 提供了一個名為 EnableCorsAttribute 的自定義屬性類。 此類包含允許的域、HTTP 方法、請求標頭、響應標頭以及是否允許使用憑據等方面的屬性(它們對前面所述的 CORS 規范的所有詳細信息進行建模)。
最后,為了讓 Web API CORS 框架處理 CORS 請求並發出適當的 CORS 響應標頭,該類必須檢查進入應用程序的每個請求。 Web API 通過消息處理程序提供用於這種攔截操作的擴展點。 Web API CORS 框架會相應地實現一個名為 CorsMessageHandler 的消息處理程序。 對於 CORS 請求,該處理程序會查詢在所調用方法的屬性中表達的策略,並發出適當的 CORS 響應標頭。
EnableCorsAttribute。EnableCorsAttribute 類就是應用程序表達其 CORS 策略的方式。EnableCorsAttribute 類有一個可接受三個或四個參數的重載構造函數。 這些參數(依次)為:
- 允許域列表
- 允許請求標頭列表
- 允許 HTTP 方法列表
- 允許響應標頭列表(可選)
還有一個允許使用憑據的屬性 (SupportsCredentials) 以及另一個用於指定預檢緩存持續時間值的屬性 (PreflightMaxAge)。
以vs2013建立的默認api程序為例,建完webapi引用程序后在Controller文件夾下會自動生成兩個控制區,一個HomeController和一個ValueController,我們主要看一下ValueControll,這個控制器繼承了ApiController,可見是一個webapi,我們在valueController上添加一個全局的 EnableCors屬性,以試它支持跨域,如下圖所示
請注意,每個構造函數參數都是一個字符串。 通過指定逗號分隔列表,可以表示多個值。 如果您想允許所有域、請求標頭或 HTTP 方法,則可以使用“*”作為值(對於響應標頭仍須顯式指定)。
除在方法級別應用 EnableCors 屬性外,還可以在類級別應用該屬性,或將其全局應用於應用程序。 應用該屬性的級別會在 Web API 代碼中為該級別及下面級別的所有請求配置 CORS。 例如,如果在方法級別應用該策略,則該策略僅應用於該操作的請求,而如果在類級別應用該策略,則該策略將應用於對該控制器的所有請求。 最后,如果全局應用該策略,則該策略將應用於所有請求。
如果在多個位置存在策略,則會使用“最接近的”屬性,並忽略其他屬性(優先順序是方法、類、全局)。 如果您已在較高級別上應用策略,但隨后想在較低級別上排除某一請求,則可以使用名為 DisableCorsAttribute 的另一個屬性類。 此屬性實質上是一個沒有允許權限的策略。
如果在您不想允許 CORS 的控制器上有其他方法,則有兩個選擇。 首先,您可在 HTTP 方法列表中顯式指定。 或者,您可以保留通配符,但使用 DisableCors 屬性來排除 Delete 方法。
CorsMessageHandler。必須為 CORS 框架啟用 CorsMessageHandler,才能執行其為評估 CORS 策略並發出 CORS 響應標頭而攔截請求的工作。 消息處理程序的啟用通常是在應用程序的 Web API 配置類中通過調用 EnableCors 擴展方法進行的:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); //啟用跨域 config.EnableCors(); } }
在瀏覽器中輸入http://localhost:19881/api/values出現如下,說明該weapi已經可用。
那么接下來,我們就要測試跨域了,新建一個mvc應用程序,制定端口號為19894
其中home/index頁面的代碼如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
@{
Layout = null;
}
<!
DOCTYPE
html>
<
html
>
<
head
>
<
meta
name="viewport" content="width=device-width" />
<
title
>Index</
title
>
<
script
src="~/Scripts/jquery-1.8.2.min.js"></
script
>
</
head
>
<
body
>
<
div
>
<
input
type="button" id="cros" value="獲取跨域" />
<
div
id="msg"></
div
>
</
div
>
<
script
type="text/javascript">
$(function () {
$("#cros").click(function () {
$.ajax({
type: "get",
success: function (d) {
$("#msg").html(d)
}
});
});
});
</
script
>
</
body
>
</
html
>
|
代碼很簡單,就防放置一個按鈕,用ajax的方式請求不同域下的webapi,返回結果如下圖所示
可以看出,其實瀏覽器發出了兩次請求。
自定義策略
從前面的示例可明顯看到,域列表(如果未使用通配符)是一個編譯到 Web API 代碼中的靜態列表。 雖然這樣做在開發過程中或在特定情況下可能行得通,但如果需要動態確定域列表或其他權限(比如,從數據庫獲取),則靜態列表就不夠用了。
明天在給大家介紹 WebApi自定義跨域策略,就是把域存在數據庫或者配置文件中,程序可以動態的修改允許的域的請求~~