zoukankan      html  css  js  c++  java
  • [转]AJAX 跨源 HTTP 请求

    转自OSChina, 原文: http://www.oschina.net/translate/ajax-cross-origin-http-request

    背景

    跨源HTTP请求(也称跨域AJAX请求)是大多数Web开发人员可能遇到的一个问题,根据同源策略,浏览器将限制客户端的JavaScript在一个安全沙箱内,通常JS不能直接同一台不同的域的远程服务器通信。在过去,开发者们创造了许多解决方法以实现跨域资源请求,常用的方法如下:

    1. 使用Flash/Silverlight或服务器端“代理”来与远程通讯 

    2. 带填充JSON (JSONP).

    3. 在iframe中嵌入远程服务器并通过fragment或window.name通信,参考这里。

    如此等等..

    这些解决方法或多或少都有问题,比如使用JSONP时若只是简单的“eval”将导致安全漏洞,#3虽然能用,但两个域间必须依据严格的协议,恕我直言它既不灵活也不优雅Smile | :)

    W3C已经引入了跨域资源共享 (CORS)作为能够解决该问题并提供安全、灵活以及推荐标准的解决方案。


     

    机制

    从较高的层次来看我们可以简单认为CORS 是介于 域A客户端 的AJAX调用 和一个托管在域B的页面 之间的契约, 一个典型的跨源 请求或者响应将会是这样:

    域 A 的 AJAX 请求头

    Host DomainB.com  
    User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0  
    Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json  
    Accept-Language en-us;  
    Accept-Encoding gzip, deflate  
    Keep-Alive 115  
     Origin http://DomainA.com 

    域 B 的 响应头

    Cache-Control private /> Content-Type application/json; charset=utf-8  
     Access-Control-Allow-Origin DomainA.com  
    Content-Length 87  
    Proxy-Connection Keep-Alive  
    Connection Keep-Alive

    我上面标记的蓝色部分是关键实现, "Origin" 请求头表示  跨源请求 或者 预检请求 源于哪里,  "Access-Control-Allow-Origin" 请求头 表示这个页面允许来自域A 的请求(其值为 * 表示允许任何域的远程请求)。

    像我上面提到的,W3 建议浏览器在提交实际跨源HTTP 请求前,实现“预检请求”, 简而言之,就是一个HTTP OPTIONS 请求:

    OPTIONS DomainB.com/foo.aspx HTTP/1.1

    如果 foo.aspx 支持 OPTIONS HTTP 指令, 它可能会像下面这样返回响应:

    HTTP/1.1 200 OK  
    Date: Wed, 01 Mar 2011 15:38:19 GMT  
    Access-Control-Allow-Origin:   http://DomainB.com  
    Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD  
    Access-Control-Allow-Headers: X-Requested-With  
    Access-Control-Max-Age: 1728000  
    Connection: Keep-Alive  
    Content-Type: application/json

    只有满足在响应中包含 "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等等并不是很实用的方法。

    参考资料

  • 相关阅读:
    Educational Codeforces Round 83 --- F. AND Segments
    Educational Codeforces Round 83 --- G. Autocompletion
    SEERC 2019 A.Max or Min
    2019-2020 ICPC Southwestern European Regional Programming Contest(Gym 102501)
    Educational Codeforces Round 78 --- F. Cards
    今天我学习了一门全新的语言
    codeforces 1323D 题解(数学)
    Educational Codeforces Round 80 (Div. 2) 题解 1288A 1288B 1288C 1288D 1288E
    Educational Codeforces Round 81 (Div. 2) 题解 1295A 1295B 1295C 1295D 1295E 1295F
    Codeforces Round #617 (Div. 3) 题解 1296C 1296D 1296E 1296F
  • 原文地址:https://www.cnblogs.com/LeoWong/p/3702399.html
Copyright © 2011-2022 走看看