【js学习笔记-099】-------HTTP脚本化

【js学习笔记-------HTTP脚本化】

 

通常,HTTP并不在脚本控制下,只是当用户单击链接,提交表单,和输入URL时才发生。但是,用js操作http是可行的,可以通过 window.location属性或调用 表单对象的submit()方法时,都会初始化http请求。在这两种情况下,浏览器会加载新页面。这种方式在多框架页面中非常有用。不过这不是本节要讨论的内容

AJAX:主要特点是脚本操纵http和web服务器来进行数据交换,不会导致页面重载。

Comet:是和AJAX相反。web服务器发起通信并异步发送消息到客户端。如果web应用需要响应服务端的消息,则它会使用ajax技术发送或请求数据。

AJAX是客户端人服务端“”数据。而Comet中,服务端向客户端“”数据(服务器推,ajax推和http流)。

<iframe>作为AJAX传输协议使用,脚本首先要把发送给web服务器的信息编码到url中,然后设置,iframe的src属性为该url。服务器能创建一个包含响应内容的html文档,并把它返回给web浏览器,并且在iframe中显示它,需要对用户不可见,例如可以使用css隐藏它。脚本能通过遍历<iframe>的文档对象来读取服务端的响应。这种方式受同源策略的影响。

<script>元素的src属性设置url并发起http get请求。使用<script>元素实现脚本操纵http是非常吸引人的,因为它们可以跨域通信而不受限于同源策略。通常,使用基于<script>的ajax传输协议时,服务器的响应采用json编码的数据格式,当执行脚本时,js解析器能自动将其“解码”。由于它使用json数据格式,因此这种ajax传输协议也叫做“JSONP”。

XMLHttpRequest对象,它定义了用脚本操纵http的API。除了常用的get请求,这个api还包含实现post请求的能力,同时它能用文本或Document对象的形式返回服务器响应。

 

Comet传输协议比AJAX更精妙,但都需要客户端和服务器之间建立连接,同时需要服务器保持连接处于打开状态,这样它才能够发送异步信息。隐藏的<iframe>能像Comet传输协议一样有用。例如,服务器以<iframe>中待执行的<script>元素的形式发送每条消息。实现Comet的一种更可靠平台方案是客户端建立一个和服务器的连接(使用ajax传输协议),同时服务器保持这个连接打开直到它需要摄推送一条消息。服务器每发送一条消息就关闭这个连接,这样可以确保客户端正确接收到消息。处理该消息之后,客户端马上为后续的消息推送建立一个新的连接。

在Ajax和Comet之上构建更高级的通信协议是可行的。例如这些客户端/服务器可以用做RPC机制或发布订阅事件系统的基础

 

使用XMLHttpRequest

这个类的每个实例都表示一个独立的请求/响应对,并且这个对象的属性和方法允许指定请求细节和提取响应数据。

W3C正在制订“2级XMLHttpRequest”标准草案。

使用:

 var request = new XMLHttpRequest();

可以重用已存在的XMLHttpRequest,但注意这将会终止之前通过该对象挂起的任何请求

MS最早把XMLHttpRequest对象引入到IE5中,且在ie5和ie6中它只是一个ActiveX对象。IE7之前的版本不支持非标准的XMLHttpRequest()构造函数。但它能做如下的模拟

  if (window.XMLHttpRequest == undefined) {
        window.XMLHttpRequest = function () {
            try {
                return new ActiveXObject("Msxml2.XMLHTTP.6.0");
            } catch (e1) {
                try {
                    return new ActiveXObject("Msxml2.XMLHTTP.3.0");
                } catch (e2) {
                    throw new Error("XMLHttpRequestis not supported");
                }
            }
        }
    }

一个http请求由4部分组成

  1. http请求方法或“动作”
  2. 正在请求的url
  3. 一个可选的请求头集合,其中可能包括身份验证信息
  4. 一个可选的请求主体

服务器返回一个http响应包含3部分

  1. 一个数字和文字组成的状态码,用来显示请求的成功和失败
  2. 一个响应头集合
  3. 响应主体

指定请求

创建了XMLHttpRequest对象之后,发起http请求的下一步是调用XMLHttpRequest对象的open()方法去指定这个请求的两个必需部分:方法url

open()的第一个参数指定http方法或动作。这个字符串不区分大小写,但通常大家用大写字母来匹配http协议。“GET”和”POST” 方法是得到广泛支持的。同时规范还支持 “DELETE”、 “HEAD”、 “OPTIONS”、 “PUT”

第二个参数是url,它是请求的主题,这是相对于文档的url,这个文档包含调用open()的脚本。如果指定绝对url、协议、主机和端口通常必须匹配所在文档的内容:跨域的请求通常会报错。

request.setRequestHeader(“Content-Type”,”text/plain”);

相同的头调用setRequestHeader()多次,新值不会取代指定的值,相反,http请求将包含这个头的多个副本或这个头将指定多个值。

 用户自己不能指定 “Content-Length”、 “Date”、 “Referer”、 “User-Agent”头。XMLHttpRequest将自动添加这些头而防止伪造它们。同样,XMLHttpRequest对象自己处理cookie、连接时间、字符集和编码判断,所以你无法身setRequestHeader()传递这些头:

Accept-Charset

Content-Transfer-Encoeding

TE

Accept-Encoeding

Date

Trailer

Connection

Expect

Transfer-Encoding

Content-Length

Host

Upgrade

Cookie

Keep-Alive

User-Agent

Cookie2

Referer

Via

 

XMLHttpRequest.send()这是请求的最后一步是指定可选的请求主体并向服务器发送它。

如:request.send(null); //GET 请求

GET请求绝对没有主体,所以应该传递null或省略这个参数。POST请求通常拥有主体同时它应该匹配使用setRequestHeader()指定的“Content-Type”头。

例:POST方法发送纯文本给服务器

fucntion  postMessage(msg){

     var request =new XMLHttpRequest(); //

     request.open(“POST”,”/log.php”);

     request.setRequestHeader(“Content-Type”,”text/plain;charset=UTF-8”);

     request.send(msg); //将msg作为请求主体发送

     //请求完成,我们将忽略任何响应和任何错误

    

 }

 

取得响应

一个完整的http响应由状态码、响应头集合、和响应主体组成。这引起都可以通过XMLHttpReqeust对象的属性和方法使用:

l  status和statusText属性以数字和文本的形式返回http状态码。如:200 404

l  使用getResponseHeader()和getAllResponseHeaders()能查询响应头。XMLHttpRequest会自动处理cookie:它会从getAllResponseHeaders()头返回集合中过滤掉的cookie头,而如果给getResponseHeader()传递”Set-Cookie”和”Set-Cookie2”则返回null

l  响应主体可以从responseText属性中得到文本形式,从responseXML属性中得到Document形式(它实际上对XHTML和XML文档有效)

监听readystatechange事件,可以用来处理响应完后需要触发的动作。

readyState是一个整数它指定了http请求的状态,同时列出了它可能的值:

常量

含义

UNSENT

0

open()尚未调用

OPENED

1

open()已调用

HEADERS_RECEIVED

2

接收到头信息

LOADING

3

接收到响应主体

DONE

4

响应完成

 

理论上,每次readyState属性改变都会触发readystatechange。实际中,当readyState改变0或1时可能没有触发这个事件。当调用send()时,即使readyState仍处于OPEND状态,也通常触发它。某些浏览器在LOADING状态时,触发多次事件给出进度反馈。当readyState值改变为4或服务器的响应完成时,所有的浏览器都触发readystatechange事件。因为在响应完成之前也会触发事件,所以事件处事程序应该一直检验readyState值。为了监听readystatechange事件,可以把事件处理函数设置为XMLHttpRequest对象的onreadystatechange属性。也能使用addEventListener()或attachEvent(),但通常每个请求只需要一个处理程序,所以只设置onreadystatechange更容易

 

例 ://发出一个http get 请求以获得指定url的内容

       //当响应成功到达,验证它是否是纯文本

       //如果是,把它传递给指定回调函数

function  getText(url,callback){

     var request = new XMLHttpRequest();

      request.open(“GET”,url);

      request.onreadystatechange = function(){

              if(request.readyState===4 &&request.status===200){

                    var type =request.getResponseHeader(“Content-Type”);

                    if(type.match(/^text/))

                        callback(request.responseText);

              }

      }

        request.send(null);

 }

 

同步响应

XMLHttpRequest对象支持同步响应。即open()的第三个参数设置为false。那么send()方法将阻塞直到请求完成。这种情况下,不需要使用事件处理程序:一旦send()返回,仅需要检查XMLHttpRequest对象的status和responseText属性如下:

        function getTextSync(url){

           var request = new XMLHttpRequest();

           request.open(“GET”,url,false); //同步

            request.send(null);

            if(request.status!==200) throw newError(request.statusText);

            var type =request.getRequestHeader(“Content-Type”);

            if(!type.match(/^text/)) throw newError(‘Expected textual response;got:’+type);

             return request.responseText;

        }

响应解码

如果服务器发送xml或xhtml文档作为其响应,你能通过responseXML属性获得一个解析形式的xml文档。这个属性的值是一个Document对象,可以使用前面学到的技术搜索和遍历它。如果服务器想发送诸如对象或数组这样的结构化数据作为其响应,它应该传输JSON编码的字符串数据。当接收它时,可以把responseText属性传递给JSON.parse()。

如下:

//发起HTTP GET响应以获取指定URL的内容

//当响应到达时,把它以解析后的xml document对象、解析后的json对象

//或字符串的形式传递给回调函数

function  getText(url,callback){

     var request = new XMLHttpRequest();

      request.open(“GET”,url);

      request.onreadystatechange = function(){

              if(request.readyState===4 &&request.status===200){

                    var type =request.getResponseHeader(“Content-Type”);

                    if(type.indexOf(“xml”)!==-1&& request.responseXML)

                        callback(request.responseXML);

                    else if(type===”application/json”)

                        callback(JSON.parse(request.responseText);

                     else

                         callback(request.responseText);

 

              }

      }

        request.send(null);

 }

 

你可以使用 XMLHttpRequest请求js脚本,然后使用全局eval()执行这个脚本。但,在这种情况下不需要使用XMLHttpRequest对象,因为<script>元素本身操纵http脚本的能力完全可以实现加载并执行脚本。且记住<script>元素能发起跨域HTTP请求,而XMLHttpRequest API则禁止

web服务器通常使用二进制数据响应http请求,resposneText属性只能用于文本,且它不能妥善处理二进制响应,XHR2定义了处理二进制响应的方法,但厂商还没实现。

如果服务器在Content-Type 头中包含错误的 “charset”参数或是没有设置Content-Type。那么XMLHttpRequest将使用错误的编码来解析响应,并且responseText中的字符可能是错的。XHR2定义了overrideMimeType()来解决这个问题;大部分厂商都实现了这个方法。在send()之前把类型传递给overrideMimeType(),这将使XMLHttpRequest忽略“Content-Type”头而使用指定的类型。

 

编码请求主体

POST请求包括一个请求主体,它包含客户端给服务器的数据。请求主体是简单的文本字符串。但,我们通常使用 http请求发送的都是更复杂的数据。

 

表单编码的请求

用户提交表单时,表单中的数据(表单元素的名和值)编码到一个字符串中并随请求发送。默认情况下,html表单,通过post方法发送给服务器,而编码后的表单数据则用作请求主体。对表单数据使用的编码方案相对简单:对每个表单元素的名字和值执行普通的url编码(使用十六进制定义码替换特殊字符),使用等号把编码后的名字和值分开。并使用“&”符号分开名值对。如:

    find=pizza&zipcode=01234&radius=1km

表单数据编码格式有一个正式的MIME类型:

        application/x-www-form-urlencode

当POST方法提交这种顺序表单数据时,必须设置“Content-Type”请求头为这个值。注意这类编码并不需要html表单,在ajax应用中,你希望发送给服务器的很可能是一个js对象。如

{

find;”pizza”

    ,zipcode:01234

    ,radius:”1km”

}

表单编码在web上如此广泛,同时所有服务器端的编程语言都能良好的支持,所以非表单数据的表单编码通常也是容易实现的事情。

如:

//编码对象的属性

//如果它们是来自html表单名/值对,使用application/x-www-form-urlencode格式

function  encodeFormData(data){

         if(!data) return “”;

         var pairs = [];

         for(var name in data){

              if(!data.hasOwnProperty(name))continue; //跳过继承属性

              if(typeof data[name]===”function”)continue; //跳过方法

              var value =data[name].toString(); //把值转换成字符串

               name =encodeURIComponent(name.replace(“%20”,”+”));

               value =encodeURIComponent(value.replace(“%20”,”+”));

               pairs.push(name+”=”+value);

               return pairs.join(“&”);

 

         }

 }

 

例如:使用表单编码数据发起一个http post请求

function  postData(url,data,callback){

      var request = new XMLHttpRequest();

      request.open(“POST”,url);

      request.onreadystatechange= functon(){

          if(request.status==200 &&request.readyState===4 && callback){

                    callback(request);

          }

      }

       request.setRequestHeader(“Content-Type”,”application/x-www-form-urlencode”);

       request.send(encodeFormData(data)); //发送表单编码的数据

}

如:使用表单编码数据发送GET请求

function  postData(url,data,callback){

      var request = new XMLHttpRequest();

      request.open(“GET”,url+”?”+ encodeFormData(data));

      request.onreadystatechange= functon(){

          if(request.status==200 &&request.readyState===4 && callback){

                    callback(request);

          }

      }

       request.setRequestHeader(“Content-Type”,”application/x-www-form-urlencode”);

      request.send(null); //发送表单编码的数据

}

JSON编码的请求

在POST请求主体中使用表单编码是常见惯例,但在任何情况下它都不是http协议的必须品。

function  postData(url,data,callback){

      var request = new XMLHttpRequest();

      request.open(“POST”,url);

      request.onreadystatechange= functon(){

          if(request.status==200 &&request.readyState===4 && callback){

                    callback(request);

          }

      }

       request.setRequestHeader(“Content-Type”,”application/json”);

       request.send(JSON.stringify(data));

}

XML编码请求

<query>

      <find zipcode=”01234” radius=”1km”>pizza</find>

</query>

function   postData(url,what,where,radius,callback){

      var request = new XMLHttpRequest();

      request.open(“POST”,url);

      request.onreadystatechange= functon(){

          if(request.status==200 &&request.readyState===4 && callback){

                    callback(request);

          }

      }

       var doc =document.implementation.createDocument(“”,”query”,null);

       var query = doc.documentElement;

       var find = doc.createElement(“find”);

       query.appendChild(find);

        find.setAttribute(“zipcode”,where);

        find.setAttribute(“radius”, radius);

        find.appendChild (doc.createTextNode(what));

        //注意将自动设置Content-Type头

       request.send(doc);

}

上传文件

html表单特性之一是当用户通过<input type=”file”>元素选择文件时,表单将在它产生的post请求主体中发送文件内容。html表单始终能上传文件,但到目前为止它还不能使用XMLHttpRequestAPI做这样的事情(目前通过formdata post)。XHR2 API允许通过send()方法传入File对象来实现上传文件。

没有File()对象构造函数,脚本仅能获取表示用户当前选择文件的File对象。在支持File对象的浏览器中,每个<input type="file">元素有一个files属性,它是File对象中的类数组对象。拖放API允许通过拖放事件的dataTransfer.files属性访问用户“拖放”到元素上的文件。可以将它当做一个用户选择文件完全不透明的表示形式,适用于通过send()来上传文件。例如下例是一个自然js函数,它对某些文件上传元素添加了change事件处理程序,这样它们能自动把任何选择过的文件内容通过POST方法自动发送到指定的URL。

//例,如:使用HTTP POST请求上传文件

//查找有data-uploadto属性的全部<inputtype="file">元素

//并注册onchange事件处理程序

//服务器的响应是忽略的

whenReady(function(){

    var elts =document.getElementsByTagName("input");

    for(var i=0;i<elts.length;i++){

         var input = elts[i];

         if(input.type!=="file")continue;

         var url = input.getAttribute("data-uploadto");//获取上传的url

         if(!url)continue;

         input.addEventListener("change",function(){

                 var file = this.files[0]; //假设单个文件选择

                 if(!file) reutrn;

                 var xhr = newXMLHttpRequest(); //创建新请求

                xhr.open("POST",url);

                 xhr.send(file); //把文件作为主体发送

          },false);

    }

 });

我们在后面的内容中可以看到文件类型是更通用的二进制大对象(Blob)类型中的一个子类型。XHR2允许向send()方法传入任何Blob对象。如果没有显示设置Content-Type头,这个Blob对象的type属性用于设置待上传的Content-Type头

multipart/form-data请求

当html表单同时包含文件上传元素和其他元素时,浏览器不能使用普通的表单编码而必须使用称为“multipart/form-data”的特殊Content-Type来用POST方法提交表单。这种编码包括使用长“边界”字符串把请求主体分离成多个部分。对于文本数据,手动创建“multipart/form-data"请求主体是可能的。但很复杂。

XHR2 定义了新的FormData API,它容易实现多部分请求主体。首先,使用FormData()构造函数创建FormData对象,然后按需多次调用这个对象的append()方法把个体“部分”(可以是字符串,File或Blob对象)添加到请求中。最后,把FormData对象传递给send()方法。send()方法将对请求定义合适的边界字符串和设置“Content-Type"头。

例:使用POST方法发送multipart/form-data请求主体

function  postFormData(url,data,callback){

      if(typeof FormData ==="undefined") throw new Error("FormData is notimplemented");

     var request  = new XMLHttpRequest();

     request.open("POST",url);

    request.onreadystatechange = function(){

       if(request.readyState==4 &&request.state==200 && callback){

           callback(request);

       }

    };

     for(var name in data){

         if(!data.hasOwnProperty(name))continue; //跳过继承属性

          var value = data[name];

          if(typeofvalue==="function") continue; //跳过方法

          //每个属性变成请求一个部分

         //这里允许File对象

         formdata.append(name,value);

     }

      request.send(formdata);

 }

 

HTTP进度事件

之前的示例使用readystatechange事件探测HTTP请求的完成。XHR2规范草案定义了更多用的事件集,有些已经在firefox、Chrome和safari中得到支持。在这个新的事件模型中,XMLHttpRequest对象在请求的不同阶段触发不同的类型事件,所以它不再需要检查readyState属性。

当调用send()时,触发loadstart事件,当加载服务器响应时,XMLHttpRequest对象会发生progress事件,通常每隔50毫秒左右,所以可以使用这些事件给用户反馈请求的进度。如果请求快速完成,它可能从不触发progress事件。当事件完成,会触发load事件。

一个完成的请求不一定是成功的请求,例如load事件的处理程序应该检查XMLHttpRequest对象的status状态码来确定收到的是“200 OK”而不是“404 Not Found”的http响应。

HTTP请求无法完成有3种情况:请求超时,会触发timeout事件。如果请求中止,会触发abort事件,最后,像太多重定向这样的网络错误会阻止请求完成,但这种情况发生时会触发error事件。

对于任何具体请求,浏览器将只会触发load、abort、timeout和error事件中的一个,XHR2规范指出一旦事件中的一个发生后,浏览器应该触发loadend事件。浏览器尚未完成loadend事件。

可以通过XMLHttpRequest对象的addEventListener()方法为这些progress事件中的每个都注册处理程序。如果每种事件只有一个事件处理程序,通常更容易的方法是只设置对应的处理程序,比如onprogress和onload。

    //测试浏览器是否支持

     if("onprogress" in (newXMLHttpRequest())){

     }

     除了像type和timestamp这样常用的Event对象属性外,与这些progress事件相关联的事件对象还有3个有用的属性。

loaded属性是目前传输的字节数值。

total属性是自“Content-Length"头传输的数据的整体长度(单位是字节)如果不知道内容长度则为0

如果知道内容长度lengthComputable属性为true;否则为false。

显示,total和loaded属性对progress事件处理程序相当有用:

request.onpregress= function(e){

     if(e.lengthComputable)progress.innerHTML =Math.round(100*e.loaded/e.total)+"% Complete;

  }

 

上传进度事件

 

除了为监控HTTP响应的加载定义的这些有用的事件外,XHR2也给出了用于监控HTTP请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest对象资将有upload属性。

upload属性是一个对象,它定义了addEventListener()方法和整个progress事件集合。(但这个对象没有定义onreadystatechange属性,upload仅能触发新的事件类型)

下例中介绍了如何使用upload progress事件把上传进度反馈给用户。

//查找所有含有“fileDropTarget”类的元素

//并注册DnD事件处理程序使它们能响应文件的拖放

//当文件放下时,上传它们到data-uploadto属性指定的URL

whenReady(function(){

var elts = document.getElementsByClassName(“fileDropTarget”);

for(var i=0;i<elts.length;i++){

     var target= elts[i];

     var url =target.getAttribute(“data-uploadto”);

      if(!url)continue;

     createFileUploadDropTarget(target,url);

 }

 function createFileUploadDropTarget(target,url){

    var uploading = false;

    console.log(target,url);

    target.οndragenter= function(e){

       console.log(“dragenter”);

        if(uploading)return ;//如果正在忙,忽略拖放

        vartypes = e.dataTransfer.types;

         if(types&& types.contains && types.contains(“Files”)) || (types.indexOf&& types.indexOf(“Files”)!==-1)){

            target.classList.add(“wantdrop”);

            return false;

         }

    }

  }

 target.ondragover = function(e){if(!uploading)return false};

 target.οndragleave=function(e){if(!uploading)target.classList.remove(“wantdrop”);}

 target.ondrop = function(e){

      if(!uploading)return false;

      var files = e.dataTransfer.files;

      if(files && files.length){

         uploading = true;

          var message = “Uploading files:<ul>”;

          for(var i=0;i<files.length;i++){message+=”<li>”+files[i].name+”</li>”;}

           message+=”</ul>”;

          target.innerHTML = message;

          target.classList.remove(“wantdrop”);

           target.classList.add(“uploading”);

         var xhr = new XMLHttpRequest();

         xhr.open(“POST”,url);

         var body = new FormData();

         for(var i=0;i<files.length;i++)body.append(i,files[i]);

         xhr.upload.onprogress = function(e){

                 if(e.lengthComputable){

                    target.innerHTML =message+Math.round(e.loaded/e.total*100)+”% Complete”;

                 }

          }

          xhr.upload.onload = function(e){

              uploading = false;

              target.classList.remove(“uploading”);

              target.innerHTML = “Drop files toupload”;

           }

           xhr.send(body);

           return false;

      }

         target.classList.remove(“wantdrop”);

  }

 });

中止请求和超时

 

中止请求和超时

可以调用XMLHttpRequest对象的abort()方法来取消正在进行的http请求。abort()方法在所有的XMLHttpRequest版本和XHR2中可用,调用abort()方法在这个对象上触发abort事件。假设使用XMLHttpRequest为文本输入域请求自动完成推荐,如果用户在服务器的建议达到之前输入了新的字符,这时等待请求不再有趣,应该中止。

XHR2定义了timeout属性来指定请求自动中止后的毫秒数,也定义了timeout事件用于当超时发生时触发。也可以用setTimeout()和abort()方法实现自己的超时。

实现超时

//发起http get请求获取指定url的内容

//如果响应成功到达,传入responseText给回调函数

//如果响应在timeout毫秒内没有到达,中止这个请求

//浏览器可能在abort()后触发"readystatechange" 如果是部分请求结果到达,甚至可能是设置status属性

//所以需要设置一个标记,当部分且超时的响应到达时不会调用回调函数,如果使用load事件就没有这个风险

  function timedGetText(url,timeout,callback){

    var request = new XMLHttpRequest();

    var timer = setTimeout(function(){

           timeout = true;

           request.abort();

    },timeout);

    request.open("GET",url);

    request.onreadystatechange = function(){

      if(request.readyState !=4) return;

      if(timeout) return ;

      if(request.status==200) callback(request.responseText);

    }

     request.send(null);

  }

 


 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值