您的位置:首页 > 编程语言 > ASP

通过扩展让ASP.NET Web API支持W3C的CORS规范(转载)

2014-03-31 16:15 585 查看
转载地址:http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-04.html

CORS(Cross-Origin Resource Sharing )跨域资源共享,让ASP.NET Web API支持JSONP和W3C的CORS规范是解决“跨域资源共享”的两种途径,在《通过扩展让ASP.NET Web API支持JSONP》中我们实现了前者,并且在《W3C的CORS Specification》一文中我们对W3C的CORS规范进行了详细介绍,现在我们通过一个具体的实例来演示如何利用ASP.NET Web API具有的扩展点来实现针对CORS的支持。


目录
一、ActionFilter OR HttpMessageHandler
二、用于定义CORS资源授权策略的特性——CorsAttribute
三、实施CORS授权检验的HttpMessageHandler——CorsMessageHandler
四、CorsMessageHandler针对简单跨域资源请求的授权检验
五、CorsMessageHandler针对Preflight Request的授权检验


一、ActionFilter OR HttpMessageHandler

通过上面针对W3C的CORS规范的介绍,我们知道跨域资源共享实现的途径就是资源的提供者利用预定义的响应报头表明自己是否将提供的资源授权给了客户端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特性。

二、用于定义CORS资源授权策略的特性——CorsAttribute

我们将具有如下定义的CorsAttribute特性直接应用到某个HttpController或者定义其中的某个Action方法上来定义相关的资源授权策略。简单起见,我们的授权策略只考虑请求站点,而忽略请求提供的自定义报头和携带的用户凭证。如下面的代码片断所示,CorsAttribute具有一个只读属性AllowOrigins表示一组被授权站点对应的Uri数组,具体站点列表在构造函数中指定。另一个只读属性ErrorMessage表示在请求没有通过授权检验情况下返回的错误消息。

[code] public class ContactsController : ApiController

{

[Cors("http://localhost:9527")]

public IHttpActionResult GetAllContacts()

{

Contact[] contacts = new Contact[]

 {

new Contact{ Name="张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},

new Contact{ Name="李四", PhoneNo="456", EmailAddress="lisi@gmail.com"},

new Contact{ Name="王五", PhoneNo="789", EmailAddress="wangwu@gmail.com"},

};

return Json<IEnumerable<Contact>>(contacts);

}

}

[/code]
[/code]
在Global.asax中,我们采用如下的方式将一个CorsMessageHandler对象添加到ASP.NET Web API的消息处理管道中。

[code]
[code] public class WebApiApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsMessageHandler ());

//其他操作

}

}

[/code]
[/code]
接下来们在MvcApp应用中定义如下一个HomeController,默认的Action方法Index会将对应的View呈现出来。

[code]
[code] public class HomeController : Controller

{

public ActionResult Index()

{

return View();

}

}

[/code]
[/code]
如下所示的是Action方法Index对应View的定义。我们的目的在于:当页面成功加载之后以Ajax请求的形式调用上面定义的Web API获取联系人列表,并将自呈现在页面上。如下面的代码片断所示,Ajax调用和返回数据的呈现是通过调用jQuery的getJSON方法完成的。在此基础上直接调用我们的ASP.NET MVC程序照样会得到如右图所示的结果.

[code]
[code] <html>

<head>

<title>联系人列表</title>

<script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>


</head>

<body>

 <ul id="contacts"></ul>

 <script type="text/javascript">

 $(function ()

 {

 var url = "http://localhost:3721/api/contacts";

 $.getJSON(url, null, function (contacts){

 $.each(contacts, function (index, contact)

{

 var html = "<li><ul>";

 html += "<li>Name: " + contact.Name + "</li>";

 html += "<li>Phone No:" + contact.PhoneNo + "</li>";

 html += "<li>Email Address: " + contact.EmailAddress + "</li>";

 html += "</ul>";

 $("#contacts").append($(html));

});

});

 });

 

</script>

</body>

</html>

[/code]
[/code]
如果我们利用Fiddler来检测针对Web API调用的Ajax请求,如下所示的请求和响应内容会被捕捉到,我们可以清楚地看到利用CorsMessageHandler添加的“Access-Control-Allow-Origin”报头出现在响应的报头集合中。

[code]
[code]GET http://localhost:3721/api/contacts HTTP/1.1

Host: localhost:3721

Connection: keep-alive

Accept: application/json, text/javascript, */*; q=0.01

Origin: http://localhost:9527[/code] 
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36

Referer: http://localhost:9527/[/code] 
Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4


HTTP/1.1 200 OK

Cache-Control: no-cache

Pragma: no-cache

Content-Length: 205

Content-Type: application/json; charset=utf-8

Expires: -1

Server: Microsoft-IIS/8.0

Access-Control-Allow-Origin: http://localhost:9527

X-AspNet-Version: 4.0.30319

X-SourceFiles: =?UTF-8?B?RTpc5oiR55qE6JGX5L2cXEFTUC5ORVQgV2ViIEFQSeahhuaetuaPnmFxOZXcgU2FtcGxlc1xDaGFwdGVyIDE0XFMxNDAzXFdlYkFwaVxhcGlcY29udGFjdHM=?=

X-Powered-By: ASP.NET

Date: Wed, 04 Dec 2013 01:50:01 GMT


[{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":wangwu@gmail.com}]

[/code]
[/code]

五、CorsMessageHandler针对Preflight Request的授权检验

从上面给出的请求和响应内容可以确定Web API的调用采用的是“简单跨域资源请求”,所以并没有采用“预检”机制。如何需要迫使浏览器采用预检机制,就需要了解我们在《W3C的CORS Specification》上面提到的简单跨域资源请求具有的两个条件

采用简单HTTP方法(GET、HEAD和POST);

不具有非简单请求报头的自定义报头。

只要打破其中任何一个条件就会迫使浏览器采用预检机制,我们选择为请求添加额外的自定义报头。在ASP.NET MVC应用用户调用Web API的View中,针对Ajax请求调用Web API的JavaScript程序被改写成如下的形式:我们在发送Ajax请求之前利用setRequestHeader函数添加了两个名称分别为“'X-Custom-Header1”和“'X-Custom-Header2”的自定义报头。

[code]
[code] <html>

<head>

<title>联系人列表</title>

<script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>



</head>


<body>


 <ul id="contacts"></ul>


 <script type="text/javascript">


 $(function ()


 {


 $.ajax({


 url : 'http://localhost:3721/api/contacts',


 type: 'GET',


 success : listContacts,


 beforeSend: setRequestHeader


});


 });




 function listContacts(contacts)


 {


 $.each(contacts, function (index, contact){


 var html = "<li><ul>";


 html += "<li>Name: " + contact.Name + "</li>";


 html += "<li>Phone No:" + contact.PhoneNo + "</li>";


 html += "<li>Email Address: " + contact.EmailAddress + "</li>";


 html += "</ul>";


 $("#contacts").append($(html));


});


 }




 function setRequestHeader(xmlHttpRequest)


 {


 xmlHttpRequest.setRequestHeader('X-Custom-Header1', 'Foo');


 xmlHttpRequest.setRequestHeader('X-Custom-Header2', 'Bar');


 }


 

</script>

</body>

</html>

[/code]
[/code]
再次运行我们的ASP.NET MVC程序,依然会得正确的输出结果,但是针对Web API的调用则会涉及到两次消息交换,分别针对预检请求和真正的跨域资源请求。从下面给出的两次消息交换涉及到的请求和响应内容可以看出:自定义的两个报头名称会出现在采用“OPTIONS”作为HTTP方法的预检请求的“Access-Control-Request-Headers”报头中,利用CorsMessageHandler添加的3个报头(“Access-Control-Allow-Origin”、“Access-Control-Allow-Methods”和“Access-Control-Allow-Headers”)均出现在针对预检请求的响应中。

[code]
[code] OPTIONS http://localhost:3721/api/contacts HTTP/1.1

Host: localhost:3721

Connection: keep-alive

Cache-Control: max-age=0

Access-Control-Request-Method: GET

Origin: http://localhost:9527[/code] 
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36

Access-Control-Request-Headers: accept, x-custom-header1, x-custom-header2

Accept: */*

Referer: http://localhost:9527/[/code] 
Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4


HTTP/1.1 200 OK

Cache-Control: no-cache

Pragma: no-cache

Expires: -1

Server: Microsoft-IIS/8.0

Access-Control-Allow-Origin: http://localhost:9527

Access-Control-Allow-Methods: *

Access-Control-Allow-Headers: accept, x-custom-header1, x-custom-header2

X-AspNet-Version: 4.0.30319

X-SourceFiles: =?UTF-8?B??=

X-Powered-By: ASP.NET

Date: Wed, 04 Dec 2013 02:11:16 GMT

Content-Length: 0


--------------------------------------------------------------------------------

GET http://localhost:3721/api/contacts HTTP/1.1

Host: localhost:3721

Connection: keep-alive

Accept: */*

X-Custom-Header1: Foo

Origin: http://localhost:9527[/code] 
X-Custom-Header2: Bar

User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36

Referer: http://localhost:9527/[/code] 
Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4


HTTP/1.1 200 OK

Cache-Control: no-cache

Pragma: no-cache

Content-Length: 205

Content-Type: application/json; charset=utf-8

Expires: -1

Server: Microsoft-IIS/8.0

Access-Control-Allow-Origin: http://localhost:9527[/code] 
X-AspNet-Version: 4.0.30319

X-SourceFiles: =?UTF-8?B?RTpc5oiR55qE6JGX5L2cXEFTUC5ORVQgV2ViIEFQSeahhuaetuaPreenmFxOZXcgU2FtcGxlc1xDaGFwdGVyIDE0XF9udGFjdHM=?=

X-Powered-By: ASP.NET

Date: Wed, 04 Dec 2013 02:11:16 GMT


[{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":wangwu@gmail.com}]

[/code]
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: