## AJAX 超文本传输协议(HyperText Transfer Protocol,HTTP)是用于从WWW服务器传输超文本到本地浏览器的传输协议(transport)。它可以使浏览器更加高效,使网络传输减少。 Ajax(Asynchronous JavaScript and XML)描述了一种主要使用脚本操纵HTTP的Web应用架构。 Ajax的主要特点是使用脚本操纵HTTP和Web服务器进行数据交换,不会导致页面重载。 AJAX技术的核心是XMLHttpRequest对象(简称XHR)。 **一、XMLHttpRequest对象** 所有浏览器(IE7之前除外)都支持XMLHttpRequest对象,它定义了用脚本操纵HTTP的API。除了常用的GET请求,这个API还包含实现POST请求的能力,同时它能用文本或Document对象的形式返回服务器的响应。 浏览器在XMLHttpRequest类上定义了它们的HTTP API,这个类的每个实例都表示一个独立的请求/响应对,并且这个对象的属性和方法允许指定请求细节和提取响应数据。 具体来说,AJAX包括以下几个步骤: - 创建AJAX对象(实例化XMLHttpRequest对象) - 发起HTTP请求 - 接收服务器传回的数据 - 更新网页数据 **1.1实例化** 实例化XMLHttpRequest对象: ``` var xhr = new XMLHttpRequest(); ``` 注意:你可以重用已存在的XMLHttpRequest,但这将会终止之前通过该对象挂起的任何请求。 由于IE7之前的版本不支持非标准的XMLHttpRequest()构造函数,不过能如下模拟: ``` function createXHR(){ if ( typeof XMLHttpRequest != 'undefined'){ return new XMLHttpRequest(); }else if(typeof ActiveXObject != 'undefined'){ if(typeof arguments.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]; }catch(e){ } } } return new ActiveXObject(arguments.callee.activeXString); }else{ throw new Error('No XHR object available'); } } ``` 一个HTTP请求由4部分组成: - HTTP请求方法或动作(verb) - 正在请求的URL - 一个可选的请求头集合,其中可能包括身份验证信息 - 一个可选的请求主体 服务器返回的HTTP响应包含3部分: - 一个数字和文字组成的状态码,用来显示请求的成功和失败 - 一个响应头集合 - 响应主体 注意:使用XMLHttpRequest时,必须把文件放到Web服务器上。因为AJAX只能向同源网址(协议、域名、端口都相同)发出HTTP请求,如果发出跨源请求,就会报错 **1.2 指定请求** **1.2.1 open()** 创建XMLHttpRequest对象后,就可以调用XMLHttpRequest对象的open()方法去指定这个请求的两个必需部分:方法和URL。 ``` xhr.open('GET','example.php'); ``` 上面代码向指定的服务器网址,启动GET请求。 open()的第一个参数指定HTTP方法或动作(常用的是“GET”和“POST”),这个字符串不区分大小写,但通常使用大写字母来匹配HTTP协议。“DELETE”、“HEAD”、“OPTONS”、“PUT”也可以作为open()方法的第1个参数。 第二个参数是URL(跨域的请求通常会报错),它是请求的主题。 还有第三个可选参数,表示是否异步发送请求的布尔值(true同步,false异步,默认false) 调用open()方法并不会真正发送请求,而只是启动一个请求以备发送。 **1.2.2 HTTP头部信息** 每个HTTP请求和响应都会带有相应的头部信息。 XHR对象也提供了操作这两种头部(即请求头部和响应头部)信息的方法。 默认情况下,在发送XHR请求的同时,还会发送下列头部信息: ``` Accept: 浏览器能够处理的内容类型 Accept-Charset: 浏览器能够显示的字符集 Accept-Encoding:浏览器能够处理的压缩编码。 Accept-Language: 浏览器当前设置的语言。 Connection:浏览器与服务器之间连接的类型 Cookie:当前页面设置的任何Cookie Host:发出请求的页面所在的域。 Referer:发出请求的页面的URI。 User-Agent:浏览器的用户代理字符串 ``` 如果有请求头,我们可以设置它。例如,POST请求需要“Content-Type”头指定请求主题的MIME类型。 ``` xhr.setRequestHeader('Content-Type','text/plain'); ``` 注意:如果对相同的头调用setRequestHeader()多次,新值不会取代之前指定的值,相反,HTTP请求将包含这个头的多个副本或这个头将指定多个值。 必须在调用open()方法只会且调用send()方法之前调用setRequestHeader()。 **1.2.3 send()** 使用XMLHttpRequest发起HTTP请求的最后一步是指定可选的请求主体的数据并向服务器发送它。 ``` xhr.send(null); ``` GET请求没有主体,所以应该传递null或者省略这个参数。POST请求通常拥有主体,同时它应该匹配使用setRequestHeader()指定的“Content-Type”头。 HTTP请求的各部分有指定顺序:请求方法和URL首先到达,然后是请求头,最后是请求主体。XMLHttpRequest实现通常直到调用send()方法才开始启动网络。 **1.2.4 GET请求** GET是最常见的请求类型,必要时,可以将查询字符串参数追加到URL的末尾,以便将信息发送给服务器。 注意:对于XHR来说,传入open()方法的URL末尾的查询字符串必须经过正确的编码,否则可能出现字符串格式的错误。 查询字符串中每个参数的名称和值都必须使用encodeURIComponent()进行编码,然后才能放到URL的末尾,而且所有名值对都必须由和号(&)分隔。如下所示: ``` xhr.open('get','example.php?name1=value1&name2=value2",true); ``` 下面这个函数可以辅助向现有的URL的末尾添加查询字符串参数: ``` function addURLParam(url, name, value){ url += (url.indexOf('?') == -1) ? '?' : '&'; url += encodeURIComponent(name) + '=' + encodeURIComponent(value); return url; } ``` **1.2.5 POST请求** 对于POST请求,应该把数据作为请求的主题提交,它不是以地址形式传参,而是在send()中传参。 注意:使用POST请求时,如果不设置Content-Type头部信息,那么发送给服务器的数据就不会出现在`$_POST`中。 **1.3 取得响应** 一个完整的HTTP响应由`状态吗`、`响应头集合`和`响应主体`组成。这些都可以通过XMLHttpRequest对象的属性和方法使用: - `status`和`statusText`属性以数字和文本的形式返回HTTP状态码。这些属性保存标准的HTTP值。像200和“OK”表示成功请求,404和“Not Found”表示URL不能匹配服务器上的任何资源。 - 使用`getResponseHeader()`和`getAllResponseHeaders()`能查询/获取响应头。XMLHttpRequest会自动处理cookie:它会从getAllResponseHeaders()头返回集合中过滤cookie头,而如果给getResponseHeader()传递“Set-Cookie”和“Set-Cookie2”,则返回null。 - 响应主体可以从responseText属性得到文本形式的,从responseXML属性中得到Document形式的。 为了在响应准备就绪时得到通知,我们必须监听XMLHttpRequest对象上的`readystatechange`事件。 每当发生状态变化的时候,readyState属性的值就会发生改变。这个值每一次变化,都会触发readyStateChange事件。 ``` xhr.onreadystatechange=function(){} ``` **1.3.1 同步响应** XMLHttpRequest默认是异步的,当然,如果有需要,我们也可以设置成同步(较少用): ``` xhr.open('GET','example.php',false); ``` 上面的代码中,通过传递第三个参数为false实现同步。要注意的是:一旦设置为同步,那么send()方法将阻塞直到请求完成。 **1.3.2 响应解码** 如果服务器发送诸如对象或数组这样的结构化数据作为响应,它应该传输JSON编码的字符串数据。 下面是一个解析不同类型的响应值的方法: ``` function get(url,callback){ var xhr = new XMLHttpRequest(); //创建新请求 xhr.open('GET',url); xhr.onreadystatechange=function(){ //如果请求完成且成功 if(xhr.readyState === 4 && xhr.status === 200){ //获得响应的类型 var type = xhr.getResponseHeader('Content-type'); if(type.indexOf('xml') !== -1 && xhr.responseXML){ callback(xhr.responseXML); //Document对象响应 }else if(type === 'application/json'){ callback(JSON.parse(xhr.responseText)); //JSON响应 }else{ callback(xhr.responseText); //字符串响应 } } }; xhr.send(null); //立即发送请求 } ``` **1.4 编码请求主体** HTTP POST请求包括一个请求主体,它包含客户端传递给服务器的数据。 **1.4.1 表单编码的请求** 默认情况下,HTML表单通过POST方法发送给服务器,而编码后的表单数据则用做请求主体。例如: ``` user=TG&age=18; ``` 表单数据编码格式有一个正式的MIME类型: ``` application/x-www-form-urlencoded ``` 当使用POST方法提交这种顺序的表单数据时,必须设置“Content-Type”请求头为这个值。 ``` xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); ``` 注意:当使用form时,这不是必需值,因为这是默认方法。 当我们发送给服务器的是一个JavaScript对象时,我们也可以采取这种类型的编码。 下面的函数encodeFormData()就是将对象转换为表单编码。 ``` /* * { * name:'TG', * age:18 * } */ 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('&'); } ``` **1.4.2 JSON编码的请求** 使用JSON编码主体来发起HTTP POST请求 ``` xhr.setRequestHeader('Content-Type','application/json'); xhr.send(JSON.stringify(data)); ``` **1.5.XMLHttpRequest实例的属性** **(1)readyState属性** `readyState`是一个整数,它指定了HTTP请求的状态。它可能的值如下: - 0,对应常量UNSENT,表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用。 - 1,对应常量OPENED,表示open()已调用,但send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息。 - 2,对应常量HEADERS_RECEIVED,表示send()方法已经执行,并且头信息和状态码已经收到。 - 3,对应常量LOADING,表示正在接收服务器传来的主体(body)部分的数据,如果responseType属性是text或者空字符串,responseText就会包含已经收到的部分信息。 - 4,对应常量DONE,表示服务器数据已经完全接收,或者本次接收已经失败了。 在IE8之前并没有定义这些值,但使用硬编码值4来表示XMLHttpRequest.DONE。 **(2)status** status属性为只读属性,表示本次请求所得到的HTTP状态码,它是一个整数。一般来说,如果通信成功的话,这个状态码是200。 ``` 200, OK,访问正常 301, Moved Permanently,永久移动 302, Move temporarily,暂时移动 304, Not Modified,未修改 307, Temporary Redirect,暂时重定向 401, Unauthorized,未授权 403, Forbidden,禁止访问 404, Not Found,未发现指定网址 500, Internal Server Error,服务器发生错误 ``` 例子: ``` if(xhr.readyState === 4){ //请求完成 if(xhr.status === 200){ //请求成功 } } ``` **(3)statusText** statusText属性为只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status属性,该属性包含整个状态信息,比如”200 OK“。 **(4)timeout** timeout属性等于一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。 对应超时,还有个监听函数: ``` xhr.ontimeout = function(){ //请求超时 } ``` **(5)response** response属性为只读,返回接收到的数据体(即body部分)。它的类型可以是ArrayBuffer、Blob、Document、JSON对象、或者一个字符串,这由XMLHttpRequest.responseType属性的值决定。 如果本次请求没有成功或者数据不完整,该属性就会等于null。 **(6)responseType** responseType属性用来指定服务器返回数据(xhr.response)的类型。 ``` ”“:字符串(默认值) “arraybuffer”:ArrayBuffer对象 “blob”:Blob对象 “document”:Document对象 “json”:JSON对象 “text”:字符串 ``` text类型适合大多数情况,而且直接处理文本也比较方便,document类型适合返回XML文档的情况,blob类型适合读取二进制数据,比如图片文件。 **(7)responseText** responseText属性返回从服务器接收到的字符串,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null。 如果服务器返回的数据格式是JSON,就可以使用responseText属性。 ``` var data = xhr.responseText; data = JSON.parse(data); ``` **(8)responseXML** responseXML属性返回从服务器接收到的Document对象,该属性为只读。如果本次请求没有成功,或者数据不完整,或者不能被解析为XML或HTML,该属性等于null。 返回的数据会被直接解析为DOM对象。 **(9)事件监听接口** XMLHttpRequest第一版,只能对`onreadystatechange`这一个事件指定回调函数。该事件对所有情况作出响应。 XMLHttpRequest第二版允许对更多的事件指定回调函数。 ``` onloadstart 请求发出 onprogress 正在发送和加载数据 onabort 请求被中止,比如用户调用了abort()方法 onerror 请求失败 onload 请求成功完成 ontimeout 用户指定的时限到期,请求还未完成 onloadend 请求完成,不管成果或失败 ``` **1.6.XMLHttpRequest实例的方法** **(1)abort()** abort方法用来终止已经发出的HTTP请求。 **(2)getAllResponseHeaders()** getAllResponseHeaders方法返回服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF分隔,如果没有受到服务器回应,该属性返回null。 **(3)getResponseHeader()** getResponseHeader方法返回HTTP头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,则该属性为null。 如果有多个字段同名,则它们的值会被连接为一个字符串,每个字段之间使用“逗号+空格”分隔。 **(4)open()** XMLHttpRequest对象的open方法用于指定发送HTTP请求的参数,它的使用格式如下,一共可以接受五个参数。 ``` void open( string method, string url, optional boolean async, optional string user, optional string password); ``` 参数说明: - method:表示HTTP动词,比如“GET”、“POST”、“PUT”和“DELETE”。 - url: 表示请求发送的网址。 - async: 格式为布尔值,默认为true,表示请求是否为异步。如果设为false,则send()方法只有等到收到服务器返回的结果,才会有返回值。 - user:表示用于认证的用户名,默认为空字符串。 - password:表示用于认证的密码,默认为空字符串。 **(5)send()** send方法用于实际发出HTTP请求。如果不带参数,就表示HTTP请求只包含头信息,也就是只有一个URL,典型例子就是GET请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。 **(6)setRequestHeader()** setRequestHeader方法用于设置HTTP头信息。该方法必须在open()之后、send()之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。 ``` xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Content-Length', JSON.stringify(data).length); xhr.send(JSON.stringify(data)); ``` 在上面代码中,首先设置头信息Content-Type,表示发送JSON格式的数据;然后设置Content-Length,表示数据长度;最后发送JSON数据。 **(6)overrideMimeType()** 该方法用来指定服务器返回数据的MIME类型。该方法必须在send()之前调用。 ``` // 强制将MIME改为文本类型 xhr.overrideMimeType('text/plain; charset=x-user-defined'); ``` 在XMLHttpRequest版本升级后,一般采用指定的responseType的方法。 ``` xhr.responseType = 'text'; ``` **2、 上传文件** 讲到上传文件,我们最常用的就是使用`<input type="file">`元素选择文件,然后表单将在它产生POST请求主体中发送文件内容。 ``` <form id="form" action="upload.php" method="POST"> <input type="file" id="files" name="photos[]"/> <button type="submit" id="upload">上传</button> </form> ``` 如果要允许选择多个文件,可设置file控件的multiple属性。 ``` <input type="file" multiple/> ``` **1.5.1 files属性** 每个`<input type="file">`元素都有一个files属性,返回一个FileList对象,它是File对象中的类数组对象,包含了用户选择的文件。 ``` var fileInput = document.getElementById('files'); var files = fileInput.files; ``` 通常情况下,对于文件上传元素,我们都会添加change事件处理程序,每次文件信息有变化,它都会触发。 ``` fileInput.addEventListener('change',function(){ var file = this.files[0]; if(!file) return; var xhr = new XMLHttpRequest(); xhr.open('POST','upload.php'); xhr.send(file); },false); ``` 文件类型是二进制大对象(Blob)类型中的一个子类型。XHR2允许向send()方法传入任何Blob对象。如果没有显式设置Content-Type头,这个Blob对象的type属性用于设置待上传的Content-Type头。 也可以显式设置: ``` xhr.setRequestHeader('Content-Type', file.type); ``` **1.5.2 multipart/form-data请求** 当HTML表单同时包含文件上传元素和其他元素时,浏览器不能使用普通的表单编码而必须使用成为“multipart/form-data”的特殊Content-Type头来用POST方法提交表单。 **1.5.3 FormData对象** XHR2定义了新的FormData API,它容易实现多部分请求主体。 首先,新建一个FormData对象的实例,用来模拟发送到服务器的表单数据,把选中的文件添加到这个对象上面。 ``` var formdata = new FormData(); ``` 然后按需多次调用这个对象的append()方法把个体“部分”添加到请求中。 ``` for(var i = 0;i < files.length; i++){ var file = files[i]; formdata.append('photos[]',file,file.name); } ``` 最后,把FormData对象传递给send()方法 ``` xhr.send(formdata); ``` 除了可以添加文件,还可以添加二进制对象(Blob)或者字符串。 ``` // Files formdata.append(name, file, filename); // Blobs formdata.append(name, blob, filename); // Strings formdata.append(name, value); ``` append方法的第一个参数是表单的控件名,第二个参数是实际的值,第三个参数是可选的,通常是文件名。 **3、HTTP进度事件** 除了使用readystatechange事件来探测HTTP请求的完成外,在XHR2中,还定义了多个有用的事件。 - abort事件:当进度事件被中止时触发。如果发生错误,导致进程中止,不会触发该事件。 - error事件:由于错误导致资源无法加载时触发。 - load事件:进度成功结束时触发。 - loadstart事件:进度开始时触发。 - loadend事件:进度停止时触发,发生顺序排在error事件\abort事件\load事件后面。 - progress事件:当操作处于进度之中,由传输的数据块不断触发。 - timeout事件:进度超过限时触发。 当调用send()时,触发单个loadstart事件。当正在加载服务器的响应时,XMLHttpRequest对象会发现progress事件,通常每隔50毫秒左右,可以使用这些事件给用户反馈请求的进度。当事件完成,会触发load事件。 HTTP请求无法完成有3种情况: - 请求超时,会触发timeout事件 - 请求终止,会触发abort事件 - 请求发生错误,会触发error事件 注意:对于任何具体请求,浏览器将只会触发load、abort、timeout和error事件中的一个。一旦这些事件中的一个发生后,浏览器应该触发 loadend 事件。 要使用这些事件,有两种方式: ``` xhr.onload=function(){} xhr.addEventListener('load',function(){}) ``` **3.1 progress事件** 因为这些事件是XHR2中才定义的,所以有时需要检查浏览器是否支持progress事件: ``` if('onprogress' in (new XMLHttpRequest())){ //支持progress事件 } ``` 除了像type和timestamp这样的常用Event对象属性外,与progress事件相关联的事件对象有3个有用的属性: - lengthComputable:返回一个布尔值,表示当前进度是否具有可计算的长度。如果为false,就表示当前进度无法测量。 - total:返回一个数值,表示当前进度的总长度。如果是通过HTTP下载某个资源,表示内容本身的长度,不含HTTP头部的长度。如果lengthComputable属性为false,则total属性就无法取得正确的值。 - loaded:返回一个数值,表示当前进度已经完成的数量。该属性除以total属性,就可以得到目前进度的百分比。 我们可以利用total和loaded属性来获取当前进度: ``` xhr.onprogress = function(e){ if(e.lengthComputable){ var percentComplete = e.loaded / e.total; } } ``` **3.2 上传进度事件** 在XHR2中,也提供了用于监控HTTP请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest对象有一个upload属性,upload属性值是一个对象,它定义了addEventListener()方法和整个progress事件集合,比如onprogress和onload。 ``` xhr.upload.onprogress = function(e){ var percentComplete = e.loaded / e.total; } ``` **3.3 中止请求和超时** 我们可以通过调用XMLHttpRequest对象的abort()方法来取消正在进行的HTTP请求,调用abort()方法时,会触发abort事件。 在XHR2中,还定义了timeout属性来指定自动中止后的毫秒数。 ``` xhr.timeout = 1000; ``` **4、同源策略** 同源策略是对JavaScript代码能够操作哪些Web内容的一条完整的安全限制。当Web页面使用多个`<iframe>`元素或打开其他浏览器窗口的时候,这一策略通常就会发挥作用。 所谓“同源”指的是”三个相同“。 - 协议相同 - 域名相同 - 端口相同 从不同Web服务器载入的文档具有不同的来源。通过同一主机的不同端口载入的文档具有不同的来源。使用http:协议载入的文档和使用https:协议载入的文档具有不同的来源,即使它们来自同一个服务器。 同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。 **4.1 不严格的同源策略** 有三种不严格的同源策略 **(1)使用Document对象的domain属性** 在默认情况下,属性domain存放的是载入文档的服务器的主机名。 比如:来自home.example.com的文档里的脚本要合法的读取developer.example.com载入的文档的属性(默认情况下是不允许的,会受到同源策略的限制)。 ``` document.domain = 'example.com'; ``` 一旦上面两个文档里包含的脚本把domain都设置成了上面相同的值,那么这两个文档就不会受同源策略的约束了,可以相互读取对方的属性。 **(2)跨域资源共享(Cross-Origin Resource Sharing)** 这种方式是应用到后台中: ``` Access-Control-Allow-Origin:*,//允许所有域名的脚本访问该资源。 Access-Control-Allow-Origin:http://www.example.com //允许特定的域名访问。 ``` **(3)跨文档消息(cross-document messaging)** 跨文档消息允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本。 **4.2 跨站脚本** 跨站脚本(Cross-site scripting,XSS),这个术语用来表示一类安全问题,也就是攻击者向目标Web站点注入HTML标签或脚本。 通常,防止XSS攻击的方式是,在使用任何不可信的数据来动态的创建文档内容之前,从中移除HTML标签。 **5、Fetch API** Fetch API是一种新规范,用来取代XMLHttpRequest对象。 **5.1 特性检测** ``` if(self.fetch){ //支持 }else{ //不支持 } ``` 在Fetch API中,最常用的就是fetch()函数,它接收一个URL参数(也可以是request对象),返回一个promise来处理response。response参数还带着一个Response对象。 ``` fetch(url).then(function(response){ console.log(response); }); ``` fetch方法还可以设置第二个参数,用来配置其他值,可选的参数有: - method: 请求使用的方法,如 GET、POST。 - headers: 请求的头信息,形式为 Headers 对象或 ByteString。 - body: 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchPara ms 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。 - mode: 请求的模式,如 cors、 no-cors 或者 same-origin。 - credentials: 请求的 credentials,如 omit、same-origin 或者 include。 - cache: 请求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached. 下面是发出POST请求 ``` fetch(url,{ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'name=TG&love=1' }).then(function(response){}) ``` 注意:fetch() 方法的参数与 Request() 构造器是一样的。 ``` fetch(input, init).then(function(response) { ... }); var myRequest = new Request(input, init); ``` 下面是一个Fetch API完整请求的简单例子: ``` fetch(url).then(function (response) { return response.json(); }).then(function (jsonData) { console.log(jsonData); }).catch(function () { console.log('出错了'); }); ``` 上面代码向指定的URL发出请求,得到回应后,将其转为JSON格式,输出到控制台。如果出错,则输出一条提示信息。 Fetch API引入三个新的对象(也是构造函数):Headers, Request和Response。 **5.2 Headers** Headers对象用来构造/读取HTTP数据包的头信息。 ``` reqHeaders = new Headers({ "Content-Type": "text/plain", "Content-Length": content.length.toString(), "X-Custom-Header": "ProcessThisImmediately", }); ``` 我们还可以使用append方法: ``` var content = 'Hello World'; var headers = new Headers(); headers.append("Accept", "application/json"); headers.append("Content-Type", "text/plain"); headers.append("Content-Length", content.length.toString()); headers.append("X-Custom-Header", "ProcessThisImmediately"); Headers对象实例还提供了一些方法: reqHeaders.has("Content-Type") // true reqHeaders.has("Set-Cookie") // false reqHeaders.set("Content-Type", "text/html") reqHeaders.append("X-Custom-Header", "AnotherValue") reqHeaders.get("Content-Length") // 11 reqHeaders.getAll("X-Custom-Header") // ["ProcessThisImmediately", "AnotherValue"] reqHeaders.delete("X-Custom-Header") reqHeaders.getAll("X-Custom-Header") // [] ``` 生成Header实例以后,可以将它作为第二个参数,传入Request方法。 ``` var headers = new Headers(); headers.append('Accept', 'application/json'); var request = new Request(URL, {headers: headers}); fetch(request).then(function(response) { console.log(response.headers); }); ``` **5.3 Request对象** Request对象用来构造HTTP请求。 ``` var req = new Request("/index.html"); req.method // "GET" req.url // "http://example.com/index.html" ``` Request对象的第二个参数,表示配置对象, ``` var uploadReq = new Request("/uploadImage", { method: "POST", headers: { "Content-Type": "image/png", }, body: "image data" }); ``` Request对象实例的mode属性,用来设置是否跨域,合法的值有以下三种:same-origin、no-cors(默认值)、cors。当设置为same-origin时,只能向同域的URL发出请求,否则会报错。 **5.4 Response对象** Fetch API 的Response接口呈现了对一次请求的响应数据 **5.4.1 属性** **(1)ok** 如果ok属性返回的状态码在200到299之间(即请求成功),这个属性为true,否则为false。因此,我们可以这样判断请求是否成功: ``` fetch(url).then(function(response){ if(response.ok){ //请求成功 }else{ //请求失败 } }); ``` **(2)status、statusText** status属性返回HTTP的状态码;statusText返回一个字符串,表示服务器发送的状态提示。比如通信成功时,status是200,而statusText是“OK” **(3)url** 返回完整的请求地址 **(4)type** type属性表示HTTP回应的类型。合法的值有五个basic、cors、default、error、opaque。basic表示正常的同域请求;cors表示CORS机制的跨域请求;error表示网络出错,无法取得信息,status属性为0, 如果需要在CORS机制下发出跨域请求,需要指明状态。 ``` fetch(url,{mode: 'cors'}).then(function(response){}) ``` **(5)headers** Headers对象,表示HTTP回应的头信息 **(6)body** 表示请求的内容。 Request对象和Response对象都有body属性,表示请求的内容。body属性可能是以下的数据类型。 ``` ArrayBuffer ArrayBufferView (Uint8Array等) Blob/File string URLSearchParams FormData ``` 注意:上面这些方法都只能使用一次,第二次使用就会报错,也就是说,body属性只能读取一次。Request对象和Response对象都有bodyUsed属性,返回一个布尔值,表示body是否被读取过。 如果希望多次使用body属性,可以使用Response对象和Request对象的clone方法。它必须在body还没有读取前调用,返回一个新的body,也就是说,需要使用几次body,就要调用几次clone方法。 response.clone() **(7)bodyUsed** bodyUsed属性,返回一个布尔值,表示body是否被读取过。 **5.4.2 Response对象的方法** **(1)text()、json()、FormData()、blob()、arrayBuffer()** 在Fetch API中,数据传送是以数据流(stream)的形式进行的。对于大文件,数据是一段一段得到的。 而Fetch API提供了五个数据流读取器。 ``` text():返回字符串 json():返回一个JSON对象 formData():返回一个FormData对象 blob():返回一个blob对象 arrayBuffer():返回一个二进制数组 ``` 简单例子: ``` response.json().then(function(json){ console.log(json); }) ```