ajax和comet

一,XMLHttpRequest对象
  IE5是最早引入XHR对象的浏览器,XHR对象是通过MSXML库中的一个ActiveX对象实现的
  使用MSXML库中的XHR对象,编写一个函数如下

  function ceateXHR(){
    if(typeof argument.callee.activeXString != "String"){
      var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
      for(i=0;len=versions.length;i<len;i++){
        try{
          new ActiveXObject(versions[i]);
          arguments.callee.activeXString = versions[i];
          break;
        }catch(ex){
          //跳过
        }
      }
    }
    return new ActiveXObject(argument.callee.activeXString);
  }

  IE7+,Firefox,Opera,CHrome和safari中支持原生XHR对象,
  var xhr = new XMLHttpRequest();
  为了兼容以上所有,使用下列函数

  function createXHR(){
    if(typeof XMLHttpRequest != "undefined"){
      return new XMLHttpRequest();
    }else if(typeof ActiveXobject != "undefined"){
      if(typeof argument.callee.activeXString != "String"){
        var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i.len;
        for(i=0,len=versions.length;i<len;i++){
          try{
            new ActiveXObject(versions[i]);
            argument.callee.activeXString = versions[i];
            break;
          }catch(ex){
            //跳过
          }
        }  
      }
      return new ActiveXObject(argument.callee.activeXString);
    }else{
      throw new Error("No XHR object available");
    }
  }

  创建对象,var xhr = createXHR();
1,XHR的用法
  open(请求的类型(get或post),请求的URL,是否异步发送的布尔值),
  要点:URL相对于执行代码的当前页面(可以使用绝对路径),open方法不会真的发送请求只是启动一个请求以备发送
  send(作为请求主体发送的数据),发送特定的请求,可以传人null,调用函数后请求会被派送到服务器
  如果请求时同步的,js代码会等到服务器响应之后执行,响应的数据会自动填充XHR对象的属性
    responseText:作为响应主体被返回的文本
    responseXML:如果响应的内容类型是text/xml,application/xml,这个属性会保存包含响应数据的XML DOM文档
    status:响应的http状态
    statusText:http状态的说明
  响应后,首先检查status属性,http状态200表示成功,此时responseText属性的内容就绪,内容类型正确的同时,responseXML也可以访问了
  状态304表示请求的资源没有被修改,可以直接使用浏览器中缓存的版本,
  xhr.open("get","example.text",false);
  xhr.send(null);
  if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
    alert(xhr.responseText);
  }else{
    alert("Request was unsuccessful:" + xhr.status);
  }
  一般情况下使用异步请求,此时js会继续执行不必等待服务器的响应,此时检测XHR的readyState属性,属性值如下
  0:未初始化,尚未调用open方法
  1:启动,调用了open方法,未调用send方法
  2,发送,调用了send方法,未接受到响应
  3,接收,接收到部分响应数据
  4,完成,接收到全部的响应数据,而且在客户端使用了
  只要readyState属性的值发生变化就会触发readystatechange事件,可以利用这个事件检测每次状态后的readyState的值
  必须在open()之前指定onreadystatechange事件处理程序才能保证跨浏览器兼容

  var xhr = createXHR();
  xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
      if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304{
        alert(xhr.responseText);
      }else{
        alert("Request was unsuccessful:" + xhr.status);
      }
    }
  };
  xhr.open("get","example",true);
  xhr.send(null);

  响应之前调用xhr.abort(),可以取消异步请求,由于内存原因,不建议重用XHR对象
2,HTTP头部信息
  每个http请求和响应都会带有相应的头部信息,XHR对象也提供了操作这两种头部信息的方式
  发送XHR请求的同时,会发送下列头部信息
    Accept:浏览器能够处理的内容类型
    Accept-Charset:浏览器能够显示的字符集
    Accept-Encoding:浏览器能够处理的压缩编码
    Accept-Language:浏览器当前设置的语言
    Connection:浏览器与服务器之间连接的类型
    Cookie:当前页面设置的任何cookie
    Host:发送请求的页面所在的域
    Referer:发送请求的页面的URL
    User-Agent:浏览器的用户代理字符串
    setRequestHeader(头部字段的名称,头部字段的值)方法可以自定义设置头部信息,在open和send方法之前调用此方法

    var xhr = createXHR();
    xhr.onreadystatechange = function(){
      if(xhr.readyState == 4){
        if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
          alert(xhr.reponseText);
        }else{
          alert("Request was unsuccessful :" + xhr.status);
        }
      }
    };
    xhr.open("get","example.php",true);
    xhr.setRequestHeader("MyHeader","MyValue");
    xhr.send(null);

  调用XHR对象的getResponseHeader()方法,传人头部字段名称,可以返回相应的响应头部信息
  getAllResponseHeaders()方法,取得一个包含所有头部信息的长字符串
  var myheader = xhr.getResponseHeader("myHeader");
  var allheader = xhr.getAllResponseHeader();
  服务器端,可以利用头部信息向浏览器发送额外的,结构化的数据,没有自定义的情况下,getAllResponseHeader()方法会返回如下
    Date:sun,14 Nov 2004 18:04:03 GMT
    Server: Apache/1.3.29(Unix)
    Vary:Accept
    X-Powered-By:PHP/4.3.8
    Connection:close
    Content-Type:text/html;charset=iso-8859-1
3,GET请求
  用于向服务器查询某些信息,可以将查询字符串追加到URL末尾,
  查询字符串,每个参数的名称和值使用encodeURIComponent()进行编码,而且所有名值对使用&分隔
  xhr.open("get","example.php?name1=value&name2=value2",true);
  使用下面函数向现有的URL末尾添加查询字符串参数
  function addURLParam(url,name,value){
    url += (url.indexof("?") == -1 ? "?" : &);
    url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
    return url;
  }
  使用下面函数来构建请求URL示例
  var url = "example.php";
  url = addURLParam(url,"name","Nicholas");
  xhr.open("get",url,true);
4,POST请求
  用于向服务器发送应该被保存的数据,
  post请求将数据作为请求的主体提交,请求的主体可以包含非常多的数据,格式不限
  发送post请求,要向send()方法中传人某些数据,可以是XML DOM文档,可以使字符串
  XHR模仿表单提交,首先将Content-type头部信息设置为application/x-www-form-urlencoded,然后以适当的格式创建一个字符串,可以使用serialize()     函数来创建字符串
  function submitData(){
    var xhr = createXHR();
    xhr.onreadystatechange = function(){
      if(xhr.readystate == 4){
        if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
          alert(xhr.responseText);
        }else{
          alert("Request was unsuccessful:" + xhr.status);
        }
      }
    };
  xhr.open("post","postexample.php",true);
  xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  var form = document.getElementById("user-info");
  xhr.send(serialize(form));
  }
  php文件通过$_POST取得提交的数据
    <?php
      header("Content-type:text/plain");
      echo <<<EOF
      Name:{$_POST["user-name"]}
      Email:{$_POST['user-email']}
      EOF;
    ?>
  如果不设置Content-type头部信息,那么发送给服务器的数据不会出现在$_POST超级全局变量中,
  要访问同样的数据,使用$HTTP_RAW_POST_DATA
二,XMLHttpRequest2级
1,FormData
  为了实现表单数据的序列化,FormData类型为序列化表单以及创建与表单格式相同的数据
  var data = new FormData();
  data.append("name","Nicholas");
  append()方法接收两个参数,键值对,
  通过向FormData构造函数中传人表单元素,可以用表单元素的数据预先向其中填入键值对
  var data = new FormData(document.forms[0]);
  创建了FormData实例,直接传给XHR的send()方法

  var xhr = createXHR();
  xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
      if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
        alert(xhr.responseText);
      }else{
        alert("Request was unsuccessful :" + xhr.status);
      }    
    }
  };
  xhr.open("post","postexample.php",true);
  var form = document.getElementById("user-info");
  xhr.send(new FormData(form));

  XHR对象能够识别传人的数据类型是FormData的实例,并配置适当的头部信息
2,超时设定
  XHR对象的timeout属性,表示请求在等待响应多少时间之后就终止,给timeout属性设置一个值,在超过了这个值后,
  就会调用ontimeout事件处理程序

  var xhr = createXHR();
  xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
      try{
        if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
          alert(xhr.responseText);
        }else{
          alert("Request was unsuccessful:" + xhr.status);
        }
      }catch(ex){
        //假设由ontimeout事件处理程序处理
      }
    }
  };
  xhr.open("get","timeout.php",true);
  xhr.timoeout = 1000;
  xhr.ontimeout = function(){
    alert("Request did not return in a second.");
  };
  xhr.send(null);

3,overrideMimeType()方法
  Firefox最早引入了overrideMimeType()方法,用于重写XHR响应的MIME类型,可以把响应当做XML而非纯文本来处理
  var xhr = createXHR();
  xhr.open("get","text.php",true);
  xhr.overrideMimeType("text/XML");
  xhr.send(null);
三,进度事件
  Progress Events规范定义了与客户端服务器通信有关的事件
  loadstart:在接收到响应数据的第一个字节时触发
  progress:在接收响应期间持续不断的触发
  error:在请求发生错误时触发
  abort:因为调用abort()方法而终止连接时触发
  load:在接收到完整的响应数据时触发
  loadend:在通信完成,或者触发了error,abort,load事件后触发(尚未被任何浏览器支持)
1,load事件
  firefox引入了load事件,用于替代readystatechange事件
  响应接收完毕后触发load事件,没有必要去检测readyState属性了,
  onload会接收到event对象,target属性就指向XHR对象,可以访问到XHR对象的所有方法和属性
  对于不支持的浏览器

  var xhr = createXHR();
  xhr.onload = function(){
    if((xhr.status >=200 && xhr.status <= 300) || xhr.status == 304){
      alert(xhr.responseText);
    }else{
      alert("Request was unsuccessful:" + xhr.status);
    }
  };
  xhr.open("get","alertEvents.php",true);
  xhr.send(null);

2,progress事件
事件在浏览器接收数据期间周期性触发,
onprogress事件处理程序会接收到一个evnet对象,其target属性是XHR对象
包含额外的三个属性:lengthComputable(进度信息是否可用的布尔值),position(已经接收的字符数),totalSize(根据Content-Length响应头部确定的预期字节数)

var xhr = createXHR();
xhr.onload = function(){
  if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
    alert(xhr.responsText);
  }else{
    alert("Request was unsuccessful" + xhr.status);
  }
};
xhr.onprogress = function(event){
  var divStatus = document.getElementById("status");
  if(event.lengthComputable){
    divStatus.innerHTML = "Recevied " + event.position + "Of" + event.totalSize + "bytes";
  }
};
xhr.open("get","alertEvent.php",true);
xhr.send(null);

四,跨源资源共享(CORS)
  思想是使用自定义的http头部,让浏览器与服务器进行沟通,从而决定请求或响应的成功与否
  请求的origin头部,包含请求页面的源信息(协议,域名,端口),
  如果服务器认为是可接受的,在Origin-Control-Allow-Origin头部中回发相同的源信息,如果是公共资源,可以回发*
1,IE对CORS的实现
  IE8引入XDR类型,类似XHR对象,但能实现安全可靠的跨域通信,区别如下
  cookie不会随请求发送,也不会随响应返回
  只能设置请求头部信息中的Content-type字段
  不能访问响应头部信息
  只支持GET和POST请求
  XDR使用方法,创建一个XDomainRequest的实例,调用open()方法,再调用send()方法,
  open()方法只接收两个参数,请求类型的UR,XDR请求都是异步的,
  请求返回之后会触发load事件,响应的数据保存在responseText属性中
  var xdr = new XDomainRequest();
  xdr.onload = function(){
    alert(xdr.responseText);
  };
  xdr.open("get","http://www.somewhere-else.com/page/");
  xdr.send(null);
  接收到响应后,只能访问响应的原始文本,没有办法确定响应的状态代码,只要响应有效就会会触发load事件,
  失败就会触发error事件,遗憾的是,除了错误本身外,没有其他信息可用,因此唯一能够确定的就是请求未成功,
  检测错误可以指定一个onerror事件处理程序
  var xdr = new XDomainRequest();
  xdr.onload = function(){alert(xdr.responseText)};
  xdr.onerror = function(){alert("An error occurred.")};
  xdr.open("get","http://www.somewhere-else.com/page/");
  xdr.send(null);
  返回请求前调用abrot()方法会终止请求
  同样支持timeout属性和ontimeout事件
  var xdr = new XDomainRequest();
  xdr.onload = function(){alert(xdr.responseText);};
  xdr.onerror = function(){alert("An Error Occurred");};
  xdr.timeout = 1000;
  xdr.ontimeout = function(){alert("Request took too long.");};
  xdr.open("get","http://www.somewhere-else.com/page/");
  xdr.send(null);
  为了支持POST请求,XDR对象提供了contenType属性,用来表示发送数据的格式
  var xdr = new XDomainRequest();
  xdr.onload = function(){};
  xdr.onerror = function(){};
  xdr.open("post","http://www.somewhere-else.com/page/");
  xdr.contentType = "application/x-www-form-urlencoded";
  xdr.send("name1=value1&name2=value2");
2,其他浏览器对CORS的实现
  通过XMLHttpRequest对象实现对CORS的原生支持,请求另一个域中的资源,使用标准的XHR对象并在open方法中传人绝对的URL

  var xhr = createXHR();
  xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
      if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
        alert("xhr.responseText");
      }else{
        alert("Request was unsuccessful:" + xhr.status);
      }
    }
  };
  xhr.open("get","htt://www.somewhere-else.com/page/",true);
  xhr.send(null);

  跨域XHR对象有限制,
  不能使用setRequestHeader()设置自定义头部
  不能发送和接受cookie
  调用getAllResponseHeader()方法总会返回空字符串
3,Preflighted Request
  透明服务器验证机制支持开发人员使用自定义的头部,
  使用下列高级选项来发送请求时,就会向服务器发送一个Preflight请求,使用OPTIONS方法,发送下列头部
    Origin:与简单的请求相同
    Access-Control-Request-Method:请求自身使用的方法
    Access-Control-Request-Header:自定义的头部信息,多个头部用逗号分隔
    Origin:http://www.nczonline.net
    Access-Control-Request-Method:POST
    Access-Control-Request-Header:NCZ
  发送这个请求后,服务器可以决定是否允许这种类型的请求,服务器通过在响应中发送如下头部与浏览器进行沟通
    Access-Control-Allow-Origin:与简单请求相同
    Access-Control-Allow-Method:允许的方法,多个方法以逗号分隔
    Access-Control-Allow-Header:允许的头部,多个头部以逗号分隔
    Access-Control-Max-Age:应该将这个Prefight请求缓存多长时间(秒)
    Access-Control-Allow-Origin:http://www.nczonline.net
    Access-Control-Allow-Method:POST,GET
    Access-COntrol-Allow-Header:NZC
    Access-Control-Max-Age:1728000
  第一次发送这种请求时会多一个http请求
4,带凭据的请求
  默认情况下跨源请求不会提供凭据(cookie,HTTP认证,客户端SSL证明)
  通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据
  如果服务器端接收带凭据的请求,会使用Access-Control-Allow-Credentials:true来响应
  如果服务器的响应中没有这个头部,浏览器就不会把响应交给javascript,responseText中将是空字符串status的值为0,调用onerror事件处理程序
5,跨浏览器的CORS

  function createCORSRequest(method,url){
    var xhr = new XMLHttpRequest();
    if("withCredentials" in xhr){
      xhr.open(method,url,true);
    }else if(typeof XDomainRequest != "undefined"){
      vxhr = new XDomainRequest();
      xhr.open(method,url);
    }else{
      xhr = null;
    }
    return xhr;
  }
  var request = createCORSRequest("get","http://www.somewhere-else.com/page/");
  if(request){
    request.onload = function(){
    //
  };
  request.send();
  }

  firefox,safari,chrome中的XMLHttpRequest对象和IE中XDomainRequest对象类似,共有属性方法如下
  abort(),用于停止正在进行的请求
  onerror(),用于替代onreadystatechange()检测错误
  onload(),用于替代onreadystatechange()检测成功
  responseText(),用于取得响应内容
  send(),用于发送请求
五,其他跨域技术
1,图像Ping
  使用img标签,动态的创建图像,使用onload和onerror事件处理程序来确定是否接收到了响应
  图形Ping是与服务器进行简单,单向,的跨域通信的一种方式
  请求的数据通过查询字符串形式发送的,响应的是任意内容,通常是像素图或204响应
  var img = new Image();
  img.onload = img.onerror = function(){};
  img.src = "http://www.example.com/test?name=Nicholas";
  请求从设置src属性那一刻开始,请求中发送了name参数
  图像Ping用于跟踪用户点击页面或动态广告曝光次数,
  两个缺点,只能发送get请求,无法访问服务器的响应文本,只能单向通信
2,JSONP
  被包含在函数调用中的JSON,
  callback({"name":"Nicholas"});
  JSONP包含两部分,回调函数和数据,回调函数是当响应到来时应该在页面中调用的函数,函数名一般在请求中指定
  数据就是传人回调函数中的JSON数据,例如:http://freegeoip.net/json/?callback=handleResponse,指定的函数名为handleResponse()
  JSONP是通过动态script元素使用的,使用时可以为src属性指定一个跨域URL
  function handleResponse(response){
    alert("you're at IP address" + resopnse.ip + ",which is in" + response.city + "," + response.region_name);
  }
  var script = document.createElement(script);
  script.src="http://freegeoip.net/json/?callback=handleResponse";
  document.body.insertBefore(script,document.body.firstChild);
  可以直接访问响应文本,支持在浏览器与服务器之间双向通信,但不能保证安全,确定JSONP请求是否失败不是很容易
  html5给script提供onerror事件处理程序,尚未得到任何任何浏览器支持,为此常通过设置计时器检测指定时间内是否收到了响应
3,Comet
  Ajax是一种从页面向服务器请求数据的技术,Comet是一种服务器向页面推送数据的技术
  Comet能够让信息近乎实时的被推送到页面上,非常适合处理体育比赛的分数和股票报价
  两种实现Comet的方式:长轮询和流
    长轮询:页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送,
    发送完数据之后,浏览器关闭连接,随即又发起一个到服务器的新请求,这一过程在页面打开期间持续不断
    长轮询是服务器等待发送响应后发送数据
  HTTP流:浏览器向服务器发送一个请求,服务器保持连接打开,然后周期性向浏览器发送数据
  DOM浏览器中,通过监听readystatechange事件及检测readyState的值是否为3,利用XHR对象实现流

  function createStreamingClient(url,progress,finished){
    var xhr = new XMLHttpRequest(),received = 0;
    xhr.open("get",url,true);
    xhr.onreadystatechange = function(){
      var result;
      if(xhr.readyState == 3){
        //只取得最新数据并整理计数器
        result = xhr.responseText.substring(received);
        received += result.length;
        //调用progress回调函数
        progress(result);
      }else if(xhr.readyState == 4){
        finished(xhr.responseText);
      }
    };
    xhr.send(null);
    return xhr;
  }
  var client = createStreamingClient("streaming.php",function(data){
    alert("Received:" + data);},function(data){
    alert("Done!");
  });

4,服务器发送事件
1)SSE API
  创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据,
  服务器响应的MIME类型必须是text/event-stream,支持长短轮询,和http流
  首先创建一个新的EventSource对象,并传进一个入口点:
  var source = new EventSource("Myevent.php");url要与创建对象的页面同域
  EventSource实例有个readyState属性,0表示正连接到服务器,1表示打开了连接,2表示关闭了连接
  还有open事件,在建立连接时触发,message事件,在服务器接收到新事件时触发,error事件,在无法建立连接时触发
  服务器发回的数据以字符串形式保存在event.data中
  默认情况下,EventSource对象会保持与服务器的活动连接,连接断开会重新连接
  强制断开使用close()方法,source.close()
2)事件流
  服务器事件会通过一个持久的http响应发送,这个响应的MIME类型为text/event-stream
  响应的格式为纯文本,最简单的情况是每个数据项前有data
  对于多个连续的以data开头的数据行,将作为多段数据解析,每个值之间有一个换行分隔符
  只有包含data:的数据行后有空行,才会触发messge事件,
  通过id:前缀可以给特定的事件指定一个关联的ID,位于data:行前面和后面皆可
  设置了ID,EventSource对象会跟踪上一次触发的事件,连接断开时,会向服务器发送一个包含名为last-Event-ID的特殊HTTP头部的请求,以便服务器知道     下一次触发的事件
5,Web Socket
  在一个单独的持久的连接上提供全双工,双向通信
  一个http请求发送到服务器已发起连接,取得服务器响应后,建立的连接使用http升级从http协议交换为web Socket协议
  ws://;wss://
  1)Web Socket API
  首先实例一个WebSocket对象并传人要连接的URL
  var socket = new WebSocket("ws://www.example.com/sever.php");
  必须给websocket构造函数传人绝对的URL,
  浏览器在实例化websocket对象后,马上尝试创建连接,readyState属性
    WebSocket.OPENING(0):正在建立连接
    WebSocket.OPEN(1):已经建立连接
    WebSocket.CLOSING(2):正在关闭连接
    WebSocket.CLOSE(3):已经关闭连接
  没有readystatechange事件,readyState永远是从0开始的
  关闭WebSocket,使用close方法
  2)发送和接收数据
  send()方法
  var socke = new WebSocket("ws://www.example.com/server.php");
  socket.send("hello world");
  websocket只能发送纯文本数据,对于复杂的结构,将数据序列化为JSON字符串,使用stringify()方法
  当服务器向客户端发来消息时,WebSocket对象会触发message事件,这个message事件与其他传递消息的协议类似,
  也是把返回的数据保存在event.data属性中
  socket.onmessage = function(event){
    var data = event.data;
    //处理数据
  }
  3)其他事件
    open:在成功建立连接时触发
    error:在发生错误时触发,连接不能持续
    close:在连接关闭时触发
  WebSocket对象不支持DOM2级事件监听器,必须使用DOM0级语法分别定义每个事件处理程序
  var socket = new WebSocket("ws://www.example.com/server.php");
  socket.onopen = function(){};
  socket.onerror = function(){};
  socket.onclose = function(){};
  只有close事件对象包含额外的三个属性
    wasClean:布尔值,表示连接是否已经明确的关闭
    code:服务器返回的数据状态码
    reason:字符串包含服务器发回的消息
6,SSE与Web Socket
  SSE,支持单向的从服务器读取数据
  WebSocket,支持双向通信
  SEE和XHR组合也可以实现双向通信
六,安全
  确保XHR访问的URl安全,通行的做法是验证发送请求者是否有权限访问相应的资源
  要求以SSL连接来访问可以通过XHR请求的资源
  要求每一次请求都附带经过相应算法计算得到的验证码
  要求发送POST而不是GET请求(对CSRF攻击没有作用)
  检查来源URL以确保是否可信,(对CSRF攻击没有作用,来源记录很容易伪造)
  基于cookie信息进行验证(对CSRF攻击没有作用,容易被伪造)

原文地址:https://www.cnblogs.com/b0xiaoli/p/3659658.html