最近工作比较闲,可以静下心来研究自己感兴趣的问题.AjaxPro框架是一个有点历史的.net上的Ajax框架了,使用起来非常方便,一直想了解它究竟是怎么实现的,现在终于有时间啦!
前台代码:
代码
<
body
>
<
form
id
="form1"
runat
="server"
>
<
asp:TextBox
ID
="txtData"
runat
="server"
></
asp:TextBox
>
<
input
type
="button"
onclick
="getdate()"
value
="aaaa"
/>
</
form
>
<
script
>
function
getdate() {
_Default.GetData(
function
() { alert(
0
); });
}
</
script
>
</
body
>
后台代码:
代码
public
partial
class
_Default : System.Web.UI.Page
{
protected
void
Page_Load(
object
sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax(
typeof
(_Default));
}
[AjaxPro.AjaxMethod()]
public
void
GetData() { }
}
在后台注册本页面后,会在本页面的HTML代码的顶部自动生成四个的引用标签,其中前三个为引用AjaxPro库文件,第四个会跟据不同的页面按照相同的模板生成不同名称的不同内容的JS文件
代码
<
script
type
="text/javascript"
src
="/Web/ajaxpro/prototype.ashx"
></
script
>
<
script
type
="text/javascript"
src
="/Web/ajaxpro/core.ashx"
></
script
>
<
script
type
="text/javascript"
src
="/Web/ajaxpro/converter.ashx"
></
script
>
<
script
type
="text/javascript"
src
="/Web/ajaxpro/_Default,App_Web_dgtqp_pl.ashx"
></
script
>
自动生成的脚本,可以看到,前台也会生成与后台页面类_Default同名的的JS类,前且也有个同名方法GetData:
代码
_Default_class
=
function
() {};
Object.extend(_Default_class.prototype, Object.extend(
new
AjaxPro.AjaxClass(), {
GetData:
function
() {
return
this
.invoke(
"
GetData
"
, {},
this
.GetData.getArguments().slice(
0
));
},
url:
'
/Web/ajaxpro/_Default,App_Web_dgtqp_pl.ashx
'
}));
_Default
=
new
_Default_class();
调动的过程:
1.客户端调用_Default.GetData(function() { alert(0); });
实质是调用_Default类的GetData方法,这个类在上面第四个引用的文件内
2.调用this.invoke("GetData", {}, this.GetData.getArguments().slice(0));
可以看到,this,也就是_Default类"继承"AjaxPro.AjaxClass()类,可以从core.ashx的JS脚本中看到,其实后者的invoke方法调用了AjaxPro.Request()类的invoke方法.
在这里,第一个参数为方法名,第二个参数为后台方法参数,第三个方法实际上为第一步JS调用的最后一个参数,可能什么也没有,也可能为一个后台参数,也可能为一个JS回调函数.
AjaxPro.AjaxClass()类的invoke方法:
代码
invoke:
function
(method, args, e) {
if
(e
!=
null
) {
if
(e.length
!=
6
) {
for
(; e.length
<
6
; ) { e.push(
null
); }
}
if
(e[
0
]
!=
null
&&
typeof
(e[
0
])
==
"
function
"
) {
return
AjaxPro.queue.add(
this
.url, method, args, e);
}
}
var
r
=
new
AjaxPro.Request();
r.url
=
this
.url;
return
r.invoke(method, args);
}
AjaxPro.Request()类的invoke方法:
代码
invoke:
function
(method, args, callback, context) {
this
.__start
=
new
Date().getTime();
//
if(this.xmlHttp == null) {
this
.xmlHttp
=
new
XMLHttpRequest();
//
}
this
.isRunning
=
true
;
this
.method
=
method;
this
.args
=
args;
this
.callback
=
callback;
this
.context
=
context;
var
async
=
typeof
(callback)
==
"
function
"
&&
callback
!=
AjaxPro.noOperation;
if
(async) {
if
(MS.Browser.isIE) {
this
.xmlHttp.onreadystatechange
=
this
.doStateChange.bind(
this
);
}
else
{
this
.xmlHttp.onload
=
this
.doStateChange.bind(
this
);
this
.xmlHttp.onerror
=
this
.mozerror.bind(
this
);
}
this
.onLoading(
true
);
}
var
json
=
AjaxPro.toJSON(args)
+
""
;
if
(AjaxPro.cryptProvider
!=
null
&&
typeof
AjaxPro.cryptProvider.encrypt
==
"
function
"
) {
json
=
AjaxPro.cryptProvider.encrypt(json);
}
this
.xmlHttp.open(
"
POST
"
,
this
.url, async);
this
.xmlHttp.setRequestHeader(
"
Content-Type
"
,
"
text/plain; charset=utf-8
"
);
this
.xmlHttp.setRequestHeader(
"
X-
"
+
AjaxPro.ID
+
"
-Method
"
, method);
if
(AjaxPro.token
!=
null
&&
AjaxPro.token.length
>
0
) {
this
.xmlHttp.setRequestHeader(
"
X-
"
+
AjaxPro.ID
+
"
-Token
"
, AjaxPro.token);
}
/*
if(!MS.Browser.isIE) {
this.xmlHttp.setRequestHeader("Connection", "close");
}
*/
this
.timeoutTimer
=
setTimeout(
this
.timeout.bind(
this
), AjaxPro.timeoutPeriod);
try
{
this
.xmlHttp.send(json); }
catch
(e) { }
//
IE offline exception
if
(
!
async) {
return
this
.createResponse({ error:
null
, value:
null
});
}
return
true
;
}
3.如果第一步的JS调用有回调函数,则执行return AjaxPro.queue.add(this.url, method, args, e),否则执行return r.invoke(method, args);
如果没有回调函数,则不会为this.xmlHttp.onreadystatechange设置回调.
如果有回调函数,它会将请求放入AjaxPro.queue队列中.AjaxPro.queue队列是AjaxPro.RequestQueue类,里面有个AjaxPro.Request类型的数组,表示可以并发请求.在AjaxPro.RequestQueue类的内部循环生成AjaxPro.Request类型的数组的时候,为每个AjaxPro.Request类型设置了callback方法.
4.这时请求到了服务端.由AjaxHandlerFactory类接收.
代码
public
class
AjaxHandlerFactory : IHttpHandlerFactory
{
//
Methods
public
IHttpHandler GetHandler(HttpContext context,
string
requestType,
string
url,
string
pathTranslated)
{
string
fileNameWithoutExtension
=
Path.GetFileNameWithoutExtension(context.Request.Path);
Type type
=
null
;
Exception exception
=
null
;
bool
flag
=
false
;
try
{
if
((Utility.Settings
!=
null
)
&&
Utility.Settings.UrlNamespaceMappings.Contains(fileNameWithoutExtension))
{
flag
=
true
;
type
=
Type.GetType(Utility.Settings.UrlNamespaceMappings[fileNameWithoutExtension].ToString(),
true
);
}
if
(type
==
null
)
{
type
=
Type.GetType(fileNameWithoutExtension,
true
);
}
}
catch
(Exception exception2)
{
exception
=
exception2;
}
string
str2
=
requestType;
if
(str2
!=
null
)
{
if
(
!
(str2
==
"
GET
"
))
{
if
(str2
==
"
POST
"
)
{
if
(
!
(
!
Utility.Settings.OnlyAllowTypesInList
||
flag))
{
return
null
;
}
IAjaxProcessor[] processorArray
=
new
IAjaxProcessor[] {
new
XmlHttpRequestProcessor(context, type),
new
IFrameProcessor(context, type) };
for
(
int
i
=
0
; i
<
processorArray.Length; i
++
)
{
if
(processorArray[i].CanHandleRequest)
{
if
(exception
!=
null
)
{
processorArray[i].SerializeObject(
new
NotSupportedException(
"
This method is either not marked with an AjaxMethod or is not available.
"
));
return
null
;
}
AjaxMethodAttribute[] customAttributes
=
(AjaxMethodAttribute[]) processorArray[i].AjaxMethod.GetCustomAttributes(
typeof
(AjaxMethodAttribute),
true
);
bool
useAsyncProcessing
=
false
;
HttpSessionStateRequirement readWrite
=
HttpSessionStateRequirement.ReadWrite;
if
(Utility.Settings.OldStyle.Contains(
"
sessionStateDefaultNone
"
))
{
readWrite
=
HttpSessionStateRequirement.None;
}
if
(customAttributes.Length
>
0
)
{
useAsyncProcessing
=
customAttributes[
0
].UseAsyncProcessing;
if
(customAttributes[
0
].RequireSessionState
!=
HttpSessionStateRequirement.UseDefault)
{
readWrite
=
customAttributes[
0
].RequireSessionState;
}
}
switch
(readWrite)
{
case
HttpSessionStateRequirement.ReadWrite:
if
(useAsyncProcessing)
{
return
new
AjaxAsyncHttpHandlerSession(processorArray[i]);
}
return
new
AjaxSyncHttpHandlerSession(processorArray[i]);
case
HttpSessionStateRequirement.Read:
if
(useAsyncProcessing)
{
return
new
AjaxAsyncHttpHandlerSessionReadOnly(processorArray[i]);
}
return
new
AjaxSyncHttpHandlerSessionReadOnly(processorArray[i]);
case
HttpSessionStateRequirement.None:
if
(useAsyncProcessing)
{
return
new
AjaxAsyncHttpHandler(processorArray[i]);
}
return
new
AjaxSyncHttpHandler(processorArray[i]);
}
if
(
!
useAsyncProcessing)
{
return
new
AjaxSyncHttpHandlerSession(processorArray[i]);
}
return
new
AjaxAsyncHttpHandlerSession(processorArray[i]);
}
}
}
}
else
{
switch
(fileNameWithoutExtension.ToLower())
{
case
"
prototype
"
:
return
new
EmbeddedJavaScriptHandler(
"
prototype
"
);
case
"
core
"
:
return
new
EmbeddedJavaScriptHandler(
"
core
"
);
case
"
ms
"
:
return
new
EmbeddedJavaScriptHandler(
"
ms
"
);
case
"
prototype-core
"
:
case
"
core-prototype
"
:
return
new
EmbeddedJavaScriptHandler(
"
prototype,core
"
);
case
"
converter
"
:
return
new
ConverterJavaScriptHandler();
default
:
if
(exception
!=
null
)
{
return
null
;
}
if
(
!
(
!
Utility.Settings.OnlyAllowTypesInList
||
flag))
{
return
null
;
}
return
new
TypeJavaScriptHandler(type);
}
}
}
return
null
;
}
首先来获取处理页面的类型.再判断请求方式.如果是GET请求,则返回前文的四个引用文件.如果是POST请求,则是AJAX请求.然后判断是xmlHttpRequest对象发过来的请求,还是隐藏的IFrame框架发过来的请求.然后通过
(AjaxMethodAttribute[]) processorArray[i].AjaxMethod.GetCustomAttributes(
typeof
(AjaxMethodAttribute),
true
);
来获取打了[AjaxMothod]标记的方法的标记方式,默认情况下,
public
AjaxMethodAttribute()
{
this
.useAsyncProcessing
=
false
;
this
.requireSessionState
=
HttpSessionStateRequirement.UseDefault;
}
最后跟据不同的属性组合来完成AJAX请求的处理,比如AjaxSyncHttpHandler
public
void
ProcessRequest(HttpContext context)
{
new
AjaxProcHelper(
this
.p).Run();
}
核心处理类为AjaxProcHelper
这个类代码很多,但最重要的只有两行:
静态AJAX处理方法:
o
=
this
.p.Type.InvokeMember(
this
.p.AjaxMethod.Name, BindingFlags.InvokeMethod
|
BindingFlags.Public
|
BindingFlags.Static,
null
,
null
, args);
实例AJAX处理方法:
o
=
this
.p.AjaxMethod.Invoke(obj3, args);
可以看到,是通过反射直接调用的指定方法.这也可以解释为什么即使是实例的AJAX方法,也获取不到控件的状态.因为它根本就没有去执行页面的生命周期.
5.ajax请求完成后,如果没有回调函数,则直接结束.如果有回调函数,则执行doStateChange方法,然后是createResponse方法,最后在endRequest方法中完成回调函数的调用.
以上就是一个完整的运用AjaxPro框架的AJAX请求过程.当然为了突出重点我省略了很多其它的要素.在查看这个框架的代码过程中,我感觉虽然我大致明白程序所表达的意思,但是有很多代码的编写方式让人难以理解.难道这就是传说中的混淆?
参考的文章:
ajaxpro 原理
AjaxPro实现机制探讨——Ajax是如何调用服务器端C#方法的?