JavaScript权威指南--脚本化HTTP
知识要点
超文本传输协议(HTTP)规定web浏览器如何从web服务器获取文档和向web服务器发送表单内容,以及web服务器如何响应这些请求和提交。web浏览器会处理大量的HTTP。通常,HTTP并不在脚本的控制下,只是当用户单击链接、提交表单和输入URL时才发送。
但是,用javascript代码操纵HTTP是可行的。当脚本设置window对象的location属性或调用表单对象的submit()方法时,都会初始化HTTP请求。在这种情况下,浏览器会从新加载页面。但这章讨论在没有导致web浏览器重新加载任何窗口或窗体内容的情况下,脚本如何实现浏览器与服务器之间的通信。
术语Ajax描述了一种主要使用脚本操作HTTP的web应用构架。(Ajax是Asynchronous javascript and XML的缩写,这个术语由jesse James Carrett创造,最早出现于2005年2月它发布的文章。)。Ajax应用的主要特点是使用脚本操作HTTP和web服务器进行数据交换,不会导致页面重载。避免页面重载(这是web初期的标准做法)的能力能使web应用感觉更像传统的桌面应用。web可以使用Ajax技术把用户的交互记录数据记录到服务器中;也可以是简单的显示页面,之后按需加载额外的数据和页面组件来提示应用的启动时间。
Comet是和使用脚本操作HTTP的web应用构架相关的术语(Comet这个名字是Alex Russell在2006年3月创造)。在某种意义上,Comet和Ajax相反,在Comet中,web服务器发起通信并异步发送到消息客户端。如果web应用需要相应服务器发送消息,则它会使用Ajax技术发送或请求数据。在Ajax中,客户端从服务器“拉”数据。在Comet中,服务端向客户端“推”数据。Comet还包括其他名词,如:“服务器推”,“Ajax推”,“HTTP流”。
实现Ajax和Comet的方式有很多种,而这些底层的实现有时候称为传输协议(transport)。例如:<IMG>元素有一个src属性。当脚本设置这个属性为url时,浏览器发起的HTTP请求会从这个URL下载图片。因此脚本通过设置<img>元素的src属性,且把信息图片URL的查询字符串部分,就把能经过编码的信息传递给web服务器。web服务器实际上必须返回某个图片作为请求结果,但它一定要不可可见。例如一个1*1像素的透明图片(这种类型的图片也叫网页信标(web bug)当网页信标不是与当前网页服务器而是其它服务器交流信息时,会担心隐私泄露。这种第三方的网页信标方式常用于统计点击数和网站流量分析)。
<img>元素无法实现完整的的Ajax传输协议,因为数据交换是单向的:客户端能发送数据到服务器,但服务器的响应一直是张图片导致客户端无法从中获取提取信息。然而<iframe>元素更加强大,为了把<iframe>作为Ajax传输协议使用,脚本首先要把发送给web服务器的信息编码到URL中,然后设置<iframe>的src属性为该URL。服务器创建一个包含响应内容的HTML文档。并把它返回给web浏览器。并且在<iframe>中显示它。<iframe>需要对用户不可见。可以使用css隐藏它。脚本能遍历<iframe>的文档对象来读取服务端的响应,注意,这种方法受限于13.6.2介绍的同源策略问题。
实际上,<script>元素的src属性能设置URL并发起HTTP GET请求,使用<script>元素实现脚本操纵HTTP是非常吸引人的。因为它们可以跨域通信并不受限于同源策略。通常,使用<script>的Ajax传输协议时,服务器的响应采用JSON(见6章9节)的数据格式,当执行脚本时,javascript解析器也能自动将其“解码”。由于它使用JSON数据格式,因此这种Ajax传输协议也叫“JSONP”。
虽然在<iframe>和<script>传输协议上能实现AJAX技术,但通常还有更简单的方式,一段时间来,所有的浏览器都支持XMLHttpRuquest对象,它定义了用脚本操作HTTP的API。除了常用的GET请求,这个API还包含实现POST请求能力,同时它能用文本或Document对象的形式返回服务器响应。虽然它的名字叫XMLHttpRequest API,但并没限定只能使用XML文档,他能获取任意类型的文本文档。本章第1节涵盖XMLHttpRequestAPI和本章的大部分。本章的大部分Ajax示例都将使用XMLHttpRequest对象实现协议(第1节)方案,我们也将在本章第2节演示如何基于<script>的传输协议。因为<script>有规避同源限制的能力。
XML是可选的
“Ajax”中的X表示XML。这个HTTP(XMLHttpRquest)主要客户端API在其名字中突出了XML,并且我们在后面将看到XMLHttpRequest对象的一个重要属性叫responseXML。它看起来像说明XML是脚本操纵HTTP的重要部分,但实际上不是。这些名字只是XML流行时的遗迹。当然AJax技术能和XML文档一起工作。但使用XML只是一种选择。实际上很少使用。XMLHttpRequest规范列出了这个令人困惑的名字的不足之处:对象名XMLHttpRequest是为了兼容web,虽然这个名字的每个部分都可能造成误导。首先,这个对象支持包括XML在内的任何基于文本的格式。其次,它能用于HTTP和HTTPS请求(一些实现支持除了HTTP和HTTPS之外的协议,但规范不包括这些功能)。最后它支持的请求是一个广义的概念,指定是对定义的HTTP方法的设计HTTP请求或响应的所有活动。
Comet传输协议比Ajax更精妙,但需要客户端和服务器建立(必要时重新建立)连接。同时要保存服务器连接处于打开状态。这样它才能发送异步信息。隐藏的<iframe>能像Comet传输协议一样有用。例如:如果服务器以<iframe>中执行的<script>元素的形式发生每条消息。实现Comet的一种更可靠的跨平台方案是客户端简历一个服务器连接(使用Ajax传输协议),同时服务器包装这个连接打开直到它需要推送一条信息,服务器每发送一个信息就关闭这个连接。这样就可以确保客户端正确接收到消息。处理该消息之后,客户端马上为后续的消息推送建立一个新连接。
实现可靠的跨平台Comet传输协议是非常有挑战性的,所以大部分使用Comet构架的web应用开发者依赖像Dojo这样的web框架中的传输协议。HTML5相关草案的Server-Sent事件,它用EventSource对象的形式定义了简单的Comet API。本章第3节涵盖EventSoure API并且演示了一个使用XMLHttpRequest显示的简单模拟示例。
在Ajax和Comet之上构建更高的通信协议是可行的。例如,这些客户端/服务器技术可以用作RPC(Remote Procedure Call,远程过程调用)机制或发布/订阅事件系统基础。
1.使用XMLHttpRequest
浏览器在XMLHttpRequest类上定义了它们的HTTP API。这个类的每个实例都表示一个独立的请求/响应对。并且这个对象的属性和方法允许指定请求细节和提取响应数据。
使用这个HTTP API做的第一件事就是实例化XMLHttpRequest对象。
你也可以重用已存在的XMLHttpRequest,但注意这将会终止之前通过该对象挂起的任何请求。
IE6中的XMLHttpRequest
微软最早把XMLHttpRequest对象引入到IE5中,且在IE5和6中它只是一个ActiveX对象。IE7之前的版本不支持非标准的XMLHttpRequest()构造函数,但能像以下来模拟解决:
//ie5和i6模拟XMLHttpRequest()构造函数
if (window.XMLHttpRequest === undefined) {
window.XMLHttpRequest = function() {
try {
//如果可用,则使用Active对象的最新版本
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
} catch (e1) {
try {
//否则回退早的版本
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
} catch (e2) {
//否则,抛出错误
throw new Error("XMLHttpRequest is not supported");
}
}
};
}
一个HTTP请求由4个部分组成:
- HTTP请求的方法或“动作”(verb)
- 正在请求的URL
- 一个可选的请求头集合。其中可能包含身份验证
- 一个可选的请求主体
服务器返回的HTTP请求响应包含3个部分:
- 一个数字和文字组成的状态码,用来显示成功和失败
- 一个响应头集合
- 响应主体
接下来两节我们将会展示如何设置HTTP请求的每个部分和如何查询HTTP响应的每个部分。
HTTP的基础请求/响应构架非常简单并易用使用。但在实践中会有各种各样的带来复杂的问题:客户端和服务器交换cookie,服务器重定向浏览器到其它服务器,缓存某些资源而剩下的不缓存,某些客户端通过代理服务器发送所有的请求等。XMLHttpRequest不是协议级的HTTP API而是浏览器级的API,浏览器需要考虑cookie、重定向、缓存和代理,但代码只须担心请求和响应。
测试的时候需要上传到服务器或者使用本地服务器。XMLHttpRequest用于同HTTP和HTTPS协议一起工作,很多同喜只有通过HTTP才有的。
1.1.指定请求
open()方法
你能为请求指定“Authorization”头,但通常不需要这么做。如果请求一个受密码保护的URL,把用户名和密码作为第4个和第5个参数传递给open(),则XMLHttpRequest将设置合适的头。(接下来了解open()可选的第三个参数。可选的用户名和密码参数会在第4部分介绍)。
send()方法
顺序问题
HTTP请求的各部分有指定顺序:请求方法和URL首先到达,然后是请求头,最后是请求主体。XMLHttpRequest实现通常直到调用send()方法开始启动网络。但XMLHttpRequest API的设计似乎使每个方法都写入网络流。这意味这调用XMLHttpRequest方法的顺序必须匹配HTTP请求的构架。例如setRequestHeader()方法的调用必须在调用open()之前但在调用send()之后。否则它将抛出异常。
它用POST方法发送文本字符串给服务器,并忽略服务器返回的任何响应:
/**POST方法发送纯文本给服务器**/
function postMessage(msg) {
var request = new XMLHttpRequest(); //新请求
request.open("POST", "log.php"); //用POST向服务器端发送脚本
//用请求主题发送纯文本消息
request.setRequestHeader("Content-type", //请求主体讲述纯文本
"text/plain;charset=UTF-8");
request.send(msg); //将msg作为请求主体发生
//请求完成 ,将忽略任何响应和错误
}
上个例子的send()方法启动请求,然后返回。当它等待的服务器的响应时间并不阻塞。
1.2.取得响应
使用getResponseHeader()和getAllResponseHeaders()能查询响应头。XMLHttpRequest会自动处理cookie:它会从getAllResponseHeaders()头返回集合中中过滤掉的cookie头,而如果给getResponseHeader()传递“Set-Cookie”和“Set-cookie2”则返回true。
XMLHttpRequest对象通常(除了见下面的“同步响应”节的内容)异步使用:发送请求后,send()方法立即返回,直到响应返回,前面列出的响应方法和属性才有效。为了响应准备就绪时得到通信,必须监听XMLHttpRequest对象上的readystatechange事件(或者4小节描述的XHR进度事件)。但为了理解这个事件类型,你必须理解readyState属性。
下面的例子定义了getText()函数来演示如何监听reaystatechange事件。事件处理程序首先要确保请求完成。如果这样,它会检测响应状态码来确保请求成功。然后它查找“Content-Type”头来验证响应主体是否是期望类型。如果3个条件都得到满足,它会把响应主体(以文本形式)发送给指定的回调函数。
/*获取HTTP响应的onreadysatechange*/
//发出一个HTTP GET请求以获得指定URL内容
//当响应成功到达,验证它是否是纯文本
//如果是,把它传递给指定的回调函数
function getText(url, callback) {
var request = new XMLHttpRequest(); //创建新请求
request.open("GET", url); //指定获取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); //立即发送请求
}
(1).同步响应
比较上例子的getText()函数同步代码:
//发起同步的HTTP GET请求以获得指定URL的内容
//返回响应文本,或如果请求不成功或响应不是文本就报错
function getTextSync(url) {
var request = new XMLHttpRequest(); //创建新请求
request.open("GET", url, false); //传递false实现同步
request.send(null); //立即发送请求
//如果请求不是200 OK,就报错
if (request.status !== 200) throw new Error("request.statusText");
//如果类型错误,就报错
var type = request.getResponseHeader("Content-Type");
if (!type.match(/^text/))
throw new Error("Expected texttual response: " + type);
return request.responseText;
}
同步请求是吸引人的,但应该避免使用它们。客户端javascript是单线程的,当send()方法阻塞时,它通常导致整个浏览器UI冻结。如果连接的服务器响应慢,那么用户的浏览器冻结。然而,22.4节可以接受使用同步的请求的场景。
(2)响应解码
如果服务器想发送诸如对象或数组这样的结构化数据作为其响应,它应该传输JSON编码(6.9节)的字符串数据。当接受它时,可以把responseText属性传给JSOP.parse()。示例:上面的一个例子的归纳:它实现指定URL的GET请求并当URL的内容准备就绪时把他们传递给指定的回调函数。但它不是一直传递文本,而是传递Document对象或使用JSON.parse()编码对应的对象或字符串。
/**解析HTTP响应**/
//发起http get响应以获取指定url内容
//当响应到达时,把它以解析后的XML Document对象、解析后的JSON对象或字符串的形式传递给回调函数
function get(url, callback) {
var request = new XMLHttpRequest();
request.open("GET", url); //创建新请求
request.onreadystatechange = function() { //定义事件监听器
//如果请求完成且成功
if (request.readyState === 4 && request.status === 200) {
//获取响应的类型
var type = request.getAllResponseHeaders("Content-Type");
//检测类型,这样我们不能再将带得到HTML文档
if (type.indexOf("xml") !== -1 && request.responseXML)
callback(request.responseXML); //Document对象响应
else if (type === "application/json")
callback(JSON.parse(request.responseText)); //JSON响应
else
callback(request.responseText); //字符串响应
}
};
request.send(null); //立即发送
}
上面例子检查该响应的“Content-Type”头且专门处理“application/json”影响。你可能希望特殊的编码的另一个响是“application/javascript”或“text/javascript”。你能使用XMLHttpRequest请求Javascript脚本,然后使用全局eval()(参见4.12.2节)执行这个脚本。但是,在这种情况下不需要使用XMLHttpRequest对象,因为<script>元素本身操作HTTP的脚本的能力完全可以实现加载并执行脚本。见例13-4,且记住<script>元素能发起跨域HTTP请求,而XMLHttpRequest API则禁止。
web服务端通常使用二进制数据(比如图片文件)响应HTTP请求,responseText属性只能用于文本,且它不能妥善处理二进制响应,即使对最终字符串使用了charCodeAt()方法,详情参见22.6.2节。
服务器响应的正常解码是假设服务器为这个响应发送了"Content-Type"头和正确的MIME类型。例如,如果服务器发送了XML文档但没有设置适当的MIME类型,那么XMLHttpRequest对象将不会解析它且设置responseXML属性。或者,如果服务器在“Content-Type”头中包含了错误的“charset”参数,那么XMLHttpRequest将使用错误的编码来解析响应,并且responeText的字符串可能是错的。XHR2定义了overrideMimeType()方法来解决这个问题,并且大量的浏览器已经实现了它。如果想对服务器你更了解资源的MIME类型,那么在调用send()之前把类型传递给overrideMimeType()。这将使XMLHttpRequest忽略“Content-Type”头且使用指定的类型。假设你将下载XML文件,而你计划将它当成纯文本对待。可以使用setOverrideMimeType()让XMLHttpRequest知道它不需要把文件解析成XML文档:
//不要把响应作为XML文档处理
request.overrideMimeType("text/plain;charset=utf-8")
1.3.编码请求主体
HTTP POST请求包含一个请求主体,它包含客户端传递给服务器的数据。在例18-1中,请求主体是简单的文本字符串,但是我们通常使用HTTP请求发送的都是更复杂的数据。本节演示这样做的一些方法。
⑴表单编码的请求
考虑HTML表单。当用户提交时,表单中的数据(每个表单元素的名字和值)编码到一个字符串中并随请求发送。默认的情况下,HTML表单通过POST方法发送给服务器,而编码后的表单数据则用做请求主体。对表单数据使用的编码方案相对简单:对每个表单元素的名字和执行普通的URL编码(使用十六进制转义码替换特殊字符串),使用等号把编码后的名字和值分开,并使用“&”符号分开名/值对。一个简单的编码如下这样:
find=laobeijing&mendian=3123&radius=1km
表单数据编码格式有一个正式的MIME类型:
application/x-www-form-urlencoded
当使用POST方法提交这种顺序的表单数据时,必须设置"Content-Type"请求头为这个值。
注意。这种类型的编码并不需要HTML,在本章我们实际上将不需要直接使用表单。在Ajax应用中,你希望发送给服务器的很可能是一个javascript对象。(这个对象可能从HTML表单的用户输入中得到,但这里不是问题),前面展示的数据变成javascript对象的表单的编码形式可能是:
{
find: "laobeijing",
mendian: 3123,
radius: "1km"
}
表单编码在web上是如此广泛使用,同时所有服务器端的编程语言都能得到良好的支持,所有非表单的数据的表单编码通常也是容易实现的事情。下面的例子展示了如何实现对象属性的表单编码。
/**用于HTTP请求的编码对象**/
/**
* 编码对象的属性
* 如果它们是来自HTML表单的名/值对,使用application/x-www-form-urlencode格式
**/
function encodeFormDate(data) {
if (!data) return ""; //一直返回字符串
var pair = []; //为了保存名=值对
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", "+")); //编码值
pair.push(name + "=" + value); //记住名对
}
return pair.join("&"); //返回使用“&”连接的名/值
}
使用已经定义的encodeFormData()函数,我们能容易的写出像下面的例子中的postData()函数这样的工具函数,需要注意的是,简单来说,postData()函数(在随后的示例中有相似的函数)不能处理服务器响应。当响应完成后,它传递整个XMLHttpRequest对象给指定的函数。这个回调函数赋值检测响应状态码和提取响应文本。
/**使用表单编码数据发起一个HTTP POST请求**/
function postData(url, data, callback) {
var request = new XMLHttpRequest;
request.open("POST", url);
request.onreadystatechange = function() { //简单的事情处理程序
if (request.readyState === 4 && callback) //当响应完成
callback(request); //调用回调函数
};
request.setRequestHeader("Content-type", //设置Content-type
"application/x-www-form-urlencoded");
request.send(encodeFormData(data)); //发送表单编码数据
}
表单数据同样可以通过GET请求来提交,既然表单提交的目的是为了执行只读查询,因此GET请求比POST更合适。(当提交表单的目标仅仅是一个只读查询,GET比POST更合适)GET请求从来没有主体,所以需要发送给服务器的表单编码数据“负载”需要一个URL(后跟一个问号)的查询部分。encodeFormData()工具函数也能用于这种GET请求。下面的例子演示了如何使用它。
/**使用表单数据发起GET请求**/
function getData(url, data, callback) {
var request = new XMLHttpRequest();
request.open("GET", url +
"?" + encodeFormData(data)); //通过添加编码数据获取指定url
request.onreadystatechange = function() { //简单事件处理程序
if (request.readyState === 4 && callback) callback(request);
};
request.send(null); //发送请求
}
HTML表单在提交的时候会对表单数据进行URL编码,但使用XMLHttpRequest能给我们编码自己想要的任何数据。随着服务器上的适当支持。我们的laobeijing查询数据将编码成一个清晰的url,如下:
http://restaurantfinder.example.com/02134/1km/pizza
⑵JSON编码的请求
/**使用JSON编码主体来发起HTTP POST请求**/
function postJSON(url, data, callback) {
var request = new XMLHttpRequest();
request.open("POST", url); //对指定的URL发送POST请求
request.onreadystatechange = function() { //简单的事件处理程序
if (request.readyState === 4 && callback) //当响应完成时
callback(request); //调用回调函数
};
request.setRequestHeader("Content-Type", "application/json");
request.send(JSON.stringify(data));
}
(3)XML编码的请求
javascript对象的用表单编码或JSON编码表达的pizza查询,也能用XML文档来表示它。例如,如下所示:
<query>
<find zipcode="02134" radius="1km">
pizza
</find>
</query>
1.4.HTTP进度事件
在之前的示例中,使用readystatechange事件探测HTTP请求的完成。XHR2规范草案定义了有更多有用的事件集,已经在现代主流的浏览器中得到了支持。这个新的事件模型中,XMLHttpRequest对象在请求的不同阶段触发不同的事件,所以它不再需要检查reayState属性。
在支持它们的浏览器中,这些新事件会像如下这样触发。当调用send()时,触发单个loadstart事件。当正在加载服务器的响应时,XMLHTTPRequest对象会发生progress事件,通常每隔50秒左右,所以可以使用这些事件给用户反馈请求的进度。如果请求快速完成,它可能从不会触发progress事件。当事件完成,会触发load事件。
一个完成的请求不一定是成功的请求,例如,load事件的处理程序应该检查XMLHttpRequest对象的status状态码来确定收到的是“200 OK”而不是“404 Not Found”的HTTP响应。
HTTP请求无法完成有3种情况,对应3种事件。如果请求超时,会触发timeout事件。如果请求中止,会触发abort事件。(18.1.5节包含超时和abort方法的内容。)最后,像太多重定向这样网络错误会阻止请求完成,但这些情况发生时会触发error事件。
对应任何具体请求,浏览器将只会触发load、abort、timeout、error事件中的一个。XHR2规范草案指出一旦这些事件中的一个发生后,浏览器会触发loadend事件。
可以通过XMLHttpRequest对象的addEventListener()方法为这些progress事件中的每个都注册处理程序。如果每种事件只有一个事件处理程序,通常更容易的方法是只设置对应的处理程序属性,比如onprogress和onload。甚至可以使用这些事件属性是否来存在来测试浏览器是否支持progress事件:
if("onprogress" in (new XMLHttpRequest())){
console.log("good!") //支持progress事件
}
除了像type和timestamp这样常用的Event对象属性外,与这些progress事件相关联的事件对象还有3个有用的属性。loaded属性是目前传输的字节数值。total属性是自“Content-Length”头传输的整体长度(单位是字节),如果不知道内容长度则为0。最后,如果知道内容长度则lengthComputable属性为true;否则为false。显然,total和loaded属性对progress事件处理程序相当有用:
request.onprogress = function(e) {
if (e.lengthComputable)
progress.innerHTML = Math.round(100 * e.loaded / e.total) + "% Complete";
}
上传进度事件
除了为监控HTTP响应的加载定义的这些有用的事件外,XHR2也给出了用于监控HTTP请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest对象将有upload属性。upload属性值是一个对象,它定义了addEventListener()方法和整个的propress事件集合,比如onprogress和onload。(但upload对象没有定义onreadystatechange属性,upload仅能触发新的事件类型)
你能仅仅像使用常见的progeress事件处理程序一样使用upload事件处理程序。对于XMLHttpRequest对象x,设置x.onprogress以监控响应下载进度,并设置x.upload.onprogress以监控请求的上传进度。
下面的例子使用了upload progress事件把上传进度反馈给用户。这个示例也展示了如何拖放API中获得File对象和如何使用FormData API在单个XMLHTTPRequest请求中上传多个文件。
2.借用<script>发送HTTP请求:JOSNP
本章概述提到过<script>元素可以作为一种ajax传输机制:只需设置<script>元素的src属性(假如它还没插入到document中,需要插入进去),然后浏览器就会发送一个HTTP请求以下载src属性所指向的url。使用<script>元素进行ajax传输的一个主要原因是,它不受同源策略的影响,因此可以使用它们从其它的服务器请求数据。第二个原因是包含JSON编码数据的响应体会自动解码(即,执行)。
脚本和安全
为了使用<script>元素进行AJAX传输,必须允许web页面可执行远程服务器发送过来的任何javascript代码。这意味着对不可信的服务器,不应该采取此措施。当与可信的服务器通信时,要堤防攻击者可能进入服务器中,然后黑客会接管你的网页,运行他自己的代码,并显示任何他想要的内容,这表现得就像这些内容本就来自你的网站。
假设你已经写过一个服务,它处理GET请求并返回JSON编码的数据。同源的文档可以在代码中使用XMLHttpRequest和JSON.parse()。假如在服务器上启用了CORS,跨域文档也可因通过XMR享受到该服务。不支持的话就用JSONP,JSON响应数据(理论上)是合法的JavaScript代码,当它到达时浏览器将执行它。
这就是JSONP中P的意义所在。当通过<script>元素调用数据是,响应内容必须用javascript函数名和圆括号包裹起来。而不是发送这样的一段JSON数据:
[1,2{"bukle":"my shoes"}]
它会发送这样的一个包裹后的JSON响应:
handleResponse(
[1, 2 {"bukle": "my shoes"}]
)
包裹后的响应会成为<script>元素的内容,它先判断JSON编码后的数据(毕竟就是一个javascript表达式),然后把它传递给handleResponse()函数,我们可以假设文档拿这些数据会做一些有用的事情。
为了可行起见,我们必须通过某种方式告诉服务,它正在从一个<javascript>元素调用,必须返回JSONP响应,而不应该是普通的JSON响应。这个可以通过URL中添加一个参数来实现,例如:追加“?json”(或&json)。
在实践中,支持JSONP的服务不会强制指定客户端必须实现的回调函数名称,比如handleResponse。相反,他们使用查询参数的值,允许客户端指定一个函数名,然后使用函数名去填充响应。下面的例子使用了一个名为jsonp的查询参数来指定回调函数的名称。许多支持JSONP的服务都能分辨出这个参数名。另外一个常见的参数名称是callback,为了让使用到的服务支持类似特殊的需求,就需要在代码上做一些修改了。
下面的例子定义了一个getJSOP()函数,它发送JSONP请求。这个例子有点复杂,有几点值得注意。首先,注意它是如何创建一个新的<Script>元素,设置其URL,并将它插入到文档中。正是插入操作触发HTTP请求。其次,注意下面的的例子为每个请求都创建了一个全新的内部回调函数,回调函数作为getJSONP()函数的一个属性存储起来。最后要注意的是回调函数做了一些清理工作:删除脚本元素,并删除自身。
3.基于服务器推送的Comet技术
JavaScript权威指南--脚本化HTTP的更多相关文章
- JavaScript权威指南--脚本化CSS
知识要点 客户端javascript程序员对CSS感兴趣的是因为样式可以通过脚本编程.脚本化css启用了一系列有趣的视觉效果.例如:可以创建动画让文档从右侧“滑入”.创造这些效果的javascript ...
- JavaScript权威指南--脚本化文档
知识要点 脚本化web页面内容是javascript的核心目标. 第13章和14章解释了每一个web浏览器窗口.标签也和框架由一个window对象所示.每个window对象有一个document对象, ...
- JavaScript权威指南学习笔记6
这两天主要翻看了书中的第18-22章,重点看了第17章:事件化处理,其它几章节主要是翻了下书知道有相关的概念,没有真正理解其中的内容,或者没有考虑究竟如何能把里面的内容应用到实际的项目中.说的讽刺一点 ...
- 《JavaScript权威指南(第6版)(中文版)》PDF
简介自1996年以来,JavaScript的:权威指南已为JavaScript圣经程序员,程序员指南和全面的参考,以核心语言和客户端JavaScript API的Web浏览器定义.第6版包括HTML5 ...
- JavaScript权威指南 - 函数
函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...
- JavaScript权威指南 - 对象
JavaScript对象可以看作是属性的无序集合,每个属性就是一个键值对,可增可删. JavaScript中的所有事物都是对象:字符串.数字.数组.日期,等等. JavaScript对象除了可以保持自 ...
- JavaScript权威指南 - 数组
JavaScript数组是一种特殊类型的对象. JavaScript数组元素可以为任意类型,最大容纳232-1个元素. JavaScript数组是动态的,有新元素添加时,自动更新length属性. J ...
- 《javascript权威指南》读书笔记——第二篇
<javascript权威指南>读书笔记——第二篇 金刚 javascript js javascript权威指南 今天是今年的196天,分享今天的读书笔记. 第2章 词法结构 2.1 字 ...
- 《javascript权威指南》读书笔记——第一篇
<javascript权威指南>读书笔记——第一篇 金刚 javascript js javascript权威指南 由于最近想系统学习下javascript,所以开始在kindle上看这本 ...
随机推荐
- Android常用权限permission列表摘录
一个Android应用程序需要权限才能调用某些android系统的功能:一个android应用也可能被其他应用调用,因此也需要声明调用自身所需要的权限.除了平时常用的权限记得比较熟悉,还有很多的权限一 ...
- python直接赋值、浅拷贝和深拷贝
# 解: # import copy # names1=['Amir','Barry','Cgakes','Dao',[11,22,33]] # names2=names1#直接赋值,指向同一个对象 ...
- JQuery中如何使用事件来出发Ajax
$(document).ready(function(){ $("input[name='customer_name']").keydown(func ...
- IO—代码—基础及其用例
字节流:文件.图片.歌曲 使用字节流的应用场景:如果是读写的数据都不需要转换成字符的时候,则使用字节流. 字节流处理单元为1个字节, 操作字节和字节数组.不能直接处理Unicode字符 字节流可用于任 ...
- Git—使用方法
1.:插件的安装(eclipse LUNA版本之后已经自动集成,不需要安装插件). * 先打开该网页提供了对应版本的EGit,自己选择相应的版本.(http://wiki.eclipse.org/EG ...
- jstack生成的Thread Dump日志结构解析
1 第一部分:Full thread dump identifier 2 第二部分:Java EE middleware, third party & custom application T ...
- Python: 列表推导式--轻量级循环
定义: 列表推导式(list comprehension)是利用其他列表创建新列表的一种方法,其工作方式类似于for循环,对列表进行过滤变种操作 eg1: >>> [x*x for ...
- shell脚本简单实例
1.模拟linnux登录shell #/bin/bashecho -n "login:" read nameecho -n "password:"read pa ...
- Python入门之python装饰器的4种类型:函数装饰函数、函数装饰类、类装饰函数、类装饰类
一:函数装饰函数 def wrapFun(func): def inner(a, b): print('function name:', func.__name__) r = func(a, b) r ...
- android 接受系统锁屏广播,及高版本发送广播
protected BroadcastReceiver messageReceiver = new BroadcastReceiver() { @Override public void onRece ...