平时服务器端开发人员写好后台之后一般写一份简单的接口说明页面,类似:
<div><label for="">param_1</label><input type="text" name="param_1" value="value_1"/></div>
<div><label for="">param_2</label><input type="text" name="param_2" value="value_2"/></div>
<div><label for="">param_3</label><input type="text" name="param_3" value="value_3"/></div>
<div><label for="">param_4</label><input type="text" name="param_4" value="value_4"/></div>
<div><input type="submit" value="submit"/></div>
</form>
由于结果是以json形式返回的,不容易一眼辨认,所以为了方便,对结果进行了简单的处理:
1,由于不能控制返回结果的页面,所以直接对请求进行了拦截并用ajax方式进行重发。
2,格式化返回的json结果,非json结果直接显示。
注:ubuntu下的chromium在处理overflow的问题上貌似有点不一样,所以结果容器写得有点罗嗦。
具体例子:
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div id="page">
<form action="/blog_article/test.html" accept-charset="utf-8">
<div><label for="">param_1</label><input type="text" name="param_1" value="value_1"/></div>
<div><label for="">param_2</label><input type="text" name="param_2" value="value_2"/></div>
<div><label for="">param_3</label><input type="text" name="param_3" value="value_3"/></div>
<div><label for="">param_4</label><input type="text" name="param_4" value="value_4"/></div
上一篇文章,将jQuery.ajax中的一些细节补充完。这篇文章讲解如果将类AJAX方法都包装进jQuery.ajax中。下篇文章则讲解各预过滤器和分发器的细节。
为什么要包装起来?
我们知道,古老的XMLHttpRequest出于同源策略考虑,是不支持跨域的。所以,在前端想动态加载跨域Javascript脚本,通常是使用被称为Script DOM Element的方案,如:
scriptElem.src = "http://anydomain.com/A.js";
document.getElementsByTagName("head")[0].appendChild(scriptElem);
同理,JSON也无法通过XMLHttpRequest进行跨域,所以利用Script DOM Element,将JSON填入一个回调函数中来实现其跨域,也就是JSONP(JSON with padding, 填充式JSON或参数式JSON)。
实际上JSONP就是将,得到JSON后回调的函数通过GET传参告诉服务器,然后服务器拼接一段脚本,用该回调函数并参数为需要的JSON数据,如:
jQuery团队当然希望开发者开发的时候,不需要想需不需要跨域,只要直接使用就行了。
所以他们把XMLHttpRequest、Script DOM Element、JSONP包装起来,都当成AJAX来使用。
这里顺便提一下,其实现代浏览器(Firefox 3.5+、Safari 4+、Chrome等)中,通过XMLHttpRequeest实现了CORS(Cross-Origin Resource Sharing, 跨源资源共享)原生支持。也就是XMLHttpRequest在某些浏览器中,实际上是可以跨域的,只需要设置一下HTTP Response Header中的Access-Control-Allow-Origin。比如设置成通配符*。
而IE8也引入XDomainRequest也实现了CORS。
但毕竟某些浏览器不行,所以,咳咳……这不能成为一种通用方案。
怎么包装起来?
首先我们有一个山寨XHR对象,也就是jqXHR对象。通过对其添加send、abort来模拟XHR对象。
可是我们需要在不同方案执行前先处理一下特异性的东东,所以我们需要一个预过滤机制(Prefilter)来预先处理一下。
然后我么您需要知道到底应当用那一套方案来执行整个过程,所以我们需要一个分发机制(Transport)来得到最后的jqXHR对象。
inspectPrefiltersOrTransports
我们在jQuery.ajax找到了预过滤和分发机制的函数,inspectPrefiltersOrTransports。
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
//……
// 得到transport
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
然后我们看看这个函数在干些什么。
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
// 定义个参数对象
var inspected = {},
// 看看传进来的结构体是prefilters, 还是transports
// 如果是prefilters,证明这是预过滤过程,如果是transports分发过程
// 所以这个是,是不是在分发过程
seekingTransport = ( structure === transports );
function inspect( dataType ) {
var selected;
// 将inspected的dataType对应属性设置成true
inspected[ dataType ] = true;
// 遍历prefilters对应dataType对象中的所有过滤器或者转换器工厂函数
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
// 得到dataType或者转换器
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
// 如果dataType为字符串,即上面过程是过滤器
// 如果在预过滤过程
// 并且这个过滤出来的dataType不等于刚开始传进来的dataType
if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
// 将现在这个新的dataType插入到options中
options.dataTypes.unshift( dataTypeOrTransport );
// 检测新的dataType
inspect( dataTypeOrTransport );
return false;
// 否则如果在分发过程
} else if ( seekingTransport ) {
// 定义selected为dataTypeOrTransport
return !( selected = dataTypeOrTransport );
}
});
return selected;
}
// 检查dataTypes数组的第一个,如果结果是undefined,
// 则看看上面检查的是不是通配符*,如果不是则检查通配符
return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
}
我们可以看到,这个函数实际上就是从prefilters和transports中取出对应dataType的东东,然后过滤或者分发。
那么怎么定义prefilters和transports这两个对象的呢?
jQuery.ajaxPrefilter & jQuery.ajaxTransport
实际上,jQuery是通过这两个接口来定义上面两个对象的内容的,一个是定义预过滤器,另一个则定义分发器。
jQuery.ajaxTransport = addToPrefiltersOrTransports( transports );
而这两个方法都是由addToPrefiltersOrTransports生成的。
addToPrefiltersOrTransports
function
ISAPI包括扩展和过滤器两种形式,都可以利用来开发动态动态Web内容。ISAPI扩展和过滤器都以DLL形式实现,供IIS进程调用。
扩展按规范必须实现两个函数接口:GetExtensionVersion、HttpExtensionProc和一个可选函数接口:TerminateExtension;扩展和Web服务器中特定虚拟目录下的文件类型关联,可以和特定的文件后缀,比如.txt关联,也可以使用通配符*和所有文件关联。比如ASP就是一个扩展,它的实现文件是asp.dll,和.asp文件关联,客户端所有的对.asp文件的请求都将进入asp.dll扩展来处理。
过滤器按规范也必须实现两个函数接口:GetFilterVersion、HttpFilterProc和一个可选函数接口:TerminateFilter;过滤器分为站点级和全局级两种类型,前者只处理被装载的特定网站的事件,后者处理该IIS服务器上所有网站的事件。
ISAPI扩展
ISAPI扩展的可能用途
IIS6.0之前的版本只有ISAPI过滤器能接收某特定URL的所有请求,但在6.0开始后ISAPI扩展也可以截获、改变、重定向或者拒绝对某特定URL空间的所有HTTP请求。现在ISAPI扩展基本可以实现所有过滤器的功能,并且运行得更快,效率更高。所以如非特别需要,微软推荐尽量少用过滤器。请参见过滤器用途。
ISAPI扩展与IIS交互的典型流程
如果DLL不在内存中的时候,IIS装载DLL。装载DLL时windows自动调用DLL的进入,退出点函数DllMain。接着IIS调用扩展的进入点函数GetExtensionVersion。如果DLL已经装载到内存中则略过此过程。
IIS对进入的请求做最小化预处理。
IIS创建并填充一个EXTENSION_CONTROL_BLOCK结构来传输请求数据和回调函数指针到ISAPI扩展。
IIS以一个为该次请求创建的EXTENSION_CONTROL_BLOCK结构指针为参数,调用ISAPI扩展的HttpExtensionProc函数。
ISAPI根据原始设计来完成处理工作:例如,从客户端读取更多数据(在一个POST操作中),或者写http头和数据返回客户端。
ISAPI扩展退出HttpExtensionProc函数,通知IIS它完成了该次处理。对于同步操作,该函数返回HSE_STATUS_SUCCESS值;对于异步操作,返回HSE_STATUS_PENDING值。
如果Keep-Alive功能没有激活的话,当请求完成关闭连接后,IIS执行该次请求连接的相关清理工作。
如果ISAPI提供了TerminateExtension函数,一旦ISAPI扩展不再需要的时候,IIS调用TerminateExtension函数。如果IIS配置了缓存ISAPI扩展项,那么除非IIS服务器被关闭或重启,否则都不会调用TerminateExtension结束函数(IIS默认配置成缓存ISAPI扩展)。
注意:GetExtensionVersion函数不会每次请求时都调用,如果IIS配置了“缓存ISAPI扩展”选项,该函数将只调用一次。而对该扩展来说HttpExtensionProc函数则会在每次客户请求的时候被调用一次。
ISAPI扩展的开发要点
ISAPI扩展必须以DLL形式实现。
ISAPI扩展必须实现两个接口函数GetExtensionVersion, HttpExtensionProc;可选实现函数TerminateExtension。
GetExtensionVersion函数是进入点函数,在IIS装载该扩展时被调用一次。ISAPI扩展的初始化工作建议在GetExtensionVersion中进行,如内存分配,线程池创建,数据库连接等。也可以放在DLL的初始化函数DLLMAIN中,相关知识参见DLL初始化知识。
HttpExtensionProc函数是功能处理进入点函数,每次有数据需要处理时会被IIS调用。这个函数接收一个EXTENSION_CONTROL_BLOCK数据结构,其中包含了要处理的数据,函数结束返回后也使用该结构和IIS通讯。
TerminateExtension函数退