转自OSChina, 原文: http://www.oschina.net/translate/ajax-cross-origin-http-request
背景跨源HTTP请求(也称跨域AJAX请求)是大多数Web开发人员可能遇到的一个问题,根据同源策略,浏览器将限制客户端的JavaScript在一个安全沙箱内,通常JS不能直接同一台不同的域的远程服务器通信。在过去,开发者们创造了许多解决方法以实现跨域资源请求,常用的方法如下:
如此等等.. 这些解决方法或多或少都有问题,比如使用JSONP时若只是简单的“eval”将导致安全漏洞,#3虽然能用,但两个域间必须依据严格的协议,恕我直言它既不灵活也不优雅 W3C已经引入了跨域资源共享 (CORS)作为能够解决该问题并提供安全、灵活以及推荐标准的解决方案。 |
机制从较高的层次来看我们可以简单认为CORS 是介于 域A客户端 的AJAX调用 和一个托管在域B的页面 之间的契约, 一个典型的跨源 请求或者响应将会是这样: 域 A 的 AJAX 请求头
域 B 的 响应头
我上面标记的蓝色部分是关键实现, "Origin" 请求头表示 跨源请求 或者 预检请求 源于哪里, "Access-Control-Allow-Origin" 请求头 表示这个页面允许来自域A 的请求(其值为 * 表示允许任何域的远程请求)。 像我上面提到的,W3 建议浏览器在提交实际跨源HTTP 请求前,实现“预检请求”, 简而言之,就是一个HTTP OPTIONS 请求:
如果 foo.aspx 支持 OPTIONS HTTP 指令, 它可能会像下面这样返回响应:
只有满足在响应中包含 "Access-Control-Allow-Origin" , 并且其值为 "*" 或者包含提交CORS请求的域,这些强制条件的浏览器才能提交正式的跨域请求, 并在 预检结果缓存” 中缓存请求结果 。 |
|
实现让我们看一下服务器端代码,例子如下(ASP.NET和PHP) ASP.NET (C#)protected void Page_Load(object sender, EventArgs e) { String data = String.Empty; String returnJSONStr = String.Empty; switch (Request.HttpMethod) { case "GET": data = Request.QueryString["Data"]; returnJSONStr = "{"Data":"Hi remote friend, you tried to passed me data: *" + data + "* through HTTP GET."}"; break; case "POST": data = Request.Form["Data"]; returnJSONStr = "{"Data":"Hi remote friend, you tried to POST some mock data: *" + data + "* to me."}"; break; case "OPTIONS": break; default: returnBadRequestResponse(); break; } if (String.IsNullOrEmpty(data)) returnBadRequestResponse(); else { Response.AddHeader("Access-Control-Allow-Origin", "*"); Response.ContentType = "application/json"; Response.Write(returnJSONStr); } } private void returnBadRequestResponse() { Response.StatusCode = 400; Response.ContentType = "application/json"; Response.Write("{"Error":"Bad HTTP request type!"}"); }
PHP if(isset($["Data"])) { $method=$_SERVER['REQUEST_METHOD']; $data=""; if($method=="POST") { $data=$_POST["Data"]; $fakeData=new FakeData(); $fakeData->Data="Hi remote friend, you tried to POST some mock data: *"+data+"* to me."; $fakeData->Time=new DateTime("now"); } elseif($method=="GET") { $fakeData=new FakeData(); $fakeData->Data="Hi remote friend, you tried to passed me data: *"+data+"* through HTTP GET."; $fakeData->Time=new DateTime("now"); } else { RaiseError(); } header('Content-type: application/json'); $jsonStr= json_encode($fakeData); echo($jsonStr); } else { RaiseError(); } function RaiseError() { http_send_status(405); header("Status: 405 Method Not Allowed"); } /*Classes definition*/ class FakeData { public $Data; public $Time; }
客户端AJAXY发起请求代码: var cor = null; // cor stands for Cross-Origin request if (window.XMLHttpRequest) { cor = new XMLHttpRequest(); } //else if (window.XDomainRequest) { //cor = new XDomainRequest(); //} else { alert("Your browser does not support Cross-Origin request!"); return; } cor.onreadystatechange = function () { if (cor.readyState == 4) { document.getElementById('lbl').innerHTML = cor.responseText; } }; var data = 'Some fake data'; if (method == 'POST') { cor.open('POST', 'http://WayneYe.com/Demo/CORSDemo/CORSDemoServer.aspx', true); cor.withCredential = "true"; cor.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); cor.send('Data=' + data); } else if (method == 'GET') { cor.open('GET', 'http://WayneYe.com/Demo/CORSDemo/CORSDemoServer.aspx?Data=' + data, true); cor.withCredential = "true"; cor.send(null); }
JS代码适用于所有主流浏览器(IE8+, FF 3.6+, Chrome 8+),我没有用IE8所采用的XDomainObject,因为 IE8+, FF and Chrome, Safari等浏览器支持XMLHTTP请求。而且XDomainObject(XDR)似乎有很多限制(参考: http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx) 结论跨源资源共享为网站开发人员实现跨源通信提供了一个安全,灵活,标准的方案。也许是时候摈弃像JSONP,Flash,Silverlight,server bridge以及window.name等等并不是很实用的方法。 参考资料 |