AJAX通信:XHR&JSONP

部分内容学习自:Javascript权威指南

XHR

Ajax的全称是Asynchronous JavaScript And XML,即异步JavaScript和XML。Ajax通过在浏览器和服务器之间添加Ajax中间层,允许浏览器异步发送请求,同时允许动态加载服务器响应。

用户在浏览页面的同时可以发送请求,浏览器无须频繁地重新加载新页面,服务器的响应不再是整个页面内容,而只是必须更新的部分数据。Ajax 可以减轻服务器和带宽的负担,提供更好的服务响应。

Ajax应用的主要特点是使用脚本操纵HTTP和Web服务器进行数据交换,不会导致页面重载。避免页面重载(这是Web初期的标准做法)的能力使Web应用感觉更像传统的桌面应用。Web应用可以使用Ajax技术把用户的交互数据记录到服务器中;也可以开始只显示简单页面,之后按需加载额外的数据和页面组件来提升应用的启动时间。

浏览器通过 JavaScript 代码向服务器发送请求,JavaScript 代码负责解析服务器的响应数据,并把样式表加到数据上,然后在现有网页中显示出来。

XML是Ajax的核心技术吗?
以JSON作为数据交换格式不仅更加简单,而且网络传输的数据量更小,因此JSON逐渐取代了Ajax技术中XML的地位。

Ajax使用JavaScript来回传送数据,XMLHttpRequest是Ajax的核心。浏览器在XMLHttpRequest类上定义了它们的HTTP API。这个类的每个实例都表示一个独立的请求/响应对,并且这个对象的属性和方法允许指定请求细节和提取响应数据。

XMLHttpRequest不是协议级的HTTP API而是浏览器级的API。浏览器需要考虑cookie、重定向、缓存和代理,但代码只需要担心请求和响应。

JavaScript主要完成如下事情:

  • 创建XMLHttpRequest对象。通过XMLHttpRequest向服务器发送请求。
  • 创建回调函数,监视服务器响应状态,在服务器响应完成后,回调函数启动。
  • 回调函数通过DOM动态更新HTML页面。

官方API

请求设置

1
2
3
4
5
6
7
8
function reqListener () {
console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();

open()

open()的第一个参数指定HTTP方法或动作。这个字符串不区分大小写,但通常大家用大写字母来匹配HTTP协议。“GET”和“POST”方法是得到广泛支持的。“GET”用于常规请求,它适用于当URL完全指定请求资源,当请求对服务器没有任何副作用以及当服务器的响应是可缓存时。“POST”方法常用于HTML表单。它在请求主体中包含额外数据(表单数据)且这些数据常存储到服务器上的数据库中(副作用)。相同URL的重复POST请求从服务器得到的响应可能不同,同时不应该缓存使用这个方法的请求。
当提交表单的目标仅仅是一个只读查询,GET比POST更合适。

除了“GET”和“POST”之外,XMLHttpRequest规范也允许把“DELETE”、“HEAD”、“OPTIONS”和“PUT”作为o p e n()的第1个参数。(“CONNECT”、“TRACE”和“TRACK”因为安全风险已被明确禁止。)

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

通过 XMLHttpRequest 生成的请求可以有两种方式来获取数据,异步模式或同步模式。请求的类型是由这个 XMLHttpRequest 对象的 open() 方法的第三个参数async的值决定的。如果该参数的值为 false,则该 XMLHttpRequest请求以同步模式进行,否则该过程将以异步模式完成。

如果请求一个受密码保护的URL,把用户名和密码作为第4个和第5个参数传递给open()

setRequestHeader()

用post方法常需要设置头部信息,如 request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

以下首部无法自定义,有些是浏览器自动处理的,有些是为了防止伪造。

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

send()

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

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

响应获取

为了在响应准备就绪时得到通知,必须监听XMLHttpRequest对象上的readystatechange事件。每次readyState属性改变都会触发readystatechange事件。readyState值:

为了监听readystatechange事件,请把事件处理函数设置为XMLHttpRequest对象的onreadystatechange属性。也能使用addEventListener()

以下内容要会手写

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

使用getResponseHeader()和getAllResponseHeaders()能查询响应头。

status和statusText属性以数字和文本的形式返回HTTP状态码。

响应主体可以从responseText属性中得到文本形式的,从responseXML属性中得到Document形式的。对象或数组这样的结构化数据作为其响应,应该传输JSON编码,可以把responseText属性传递给JSON.parse()

JSON.parse()【从一个字符串中解析出json对象】
JSON.stringify()【从一个对象中解析出字符串】

abort()

调用abort()的主要原因是完成取消或超时请求消耗的时间太长或当响应变得无关时。假设使用XMLHttpRequest为文本输入域请求自动完成推荐。如果用户在服务器的建议达到之前输入了新字符,这时等待请求不再有趣,应该中止。


jsonp策略

通过 XHR 实现 Ajax 通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR 对象只能访 问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为。

CORS(Cross-Origin Resource Sharing,跨源资源共享)是 W3C 的一个工作草案,定义了在必须访 问跨源资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想,就是使用自定义的 HTTP 头部 让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

比如一个简单的使用 GET 或 POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain。在 发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端 口),以便服务器根据这个头部信息来决定是否给予响应。

下面是 Origin 头部的一个示例:

Origin: http://www.nczonline.net

如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源 信息(如果是公共资源,可以回发”*”)。例如:

Access-Control-Allow-Origin: http://www.nczonline.net

如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器 会处理请求。注意,请求和响应都不包含 cookie 信息。

因为同源策略,浏览器不允许原始脚本查找跨域文档的内容。使用XMLHttpRequest,文档内容都是通过responseText属性暴露,所以同源策略不允许XMLHttpRequest进行跨域请求。

有一些安全细节需要了解。首先,如果给XMLHttpRequest的open()方法传入用户名和密码,那么它们绝对不会通过跨域请求发送(这使分布式密码破解攻击成为可能)。除外,跨域请求通常也不会包含其他任何的用户证书:cookie和HTTP身份验证令牌(token)通常不会作为请求的内容部分发送且任何作为跨域响应来接收的cookie都会丢弃。如果跨域请求需要这几种凭证才能成功,那么必须在用send()发送请求前设置XMLHttpRequest的withCredentials属性为true。这样做不常见,但测试withCredentials的存在性是测试浏览器是否支持CORS的一种方法。

XMLHttpRequest只是AJAX的一种实现方法。

<script>元素并未真正受限于同源策略:它加载并执行任何来源的脚本。使得<script>元素成为取代XMLHttpRequest的主流Ajax传输协议。

只须设置<script>元素的src属性(假如它还没插入到document中,需要插入进去),插入操作触发HTTP请求以下载src属性所指向的URL。使用基于<script>的Ajax传输协议时,服务器的响应采用JSON编码的数据格式,当执行脚本时,JavaScript解析器能自动将其“解码”。由于它使用JSON数据格式,因此这种Ajax传输协议也叫做“JSONP”。

P代表“填充”或“前缀”,追加“?json”(或&json) 保证返回一个JSONP响应

为了使用<script>元素进行Ajax传输,必须允许Web页面可以执行远程服务器发送过来的任何JavaScript代码。这意味着对于不可信的服务器,不应该采取该技术