最近工作比较闲,可以静下心来研究自己感兴趣的问题.AjaxPro框架是一个有点历史的.net上的Ajax框架了,使用起来非常方便,一直想了解它究竟是怎么实现的,现在终于有时间啦!
前台代码:
<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>
后台代码:
{
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/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:
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方法:
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方法:
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类接收.
{
// 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框架发过来的请求.然后通过
来获取打了[AjaxMothod]标记的方法的标记方式,默认情况下,
{
this.useAsyncProcessing = false;
this.requireSessionState = HttpSessionStateRequirement.UseDefault;
}
最后跟据不同的属性组合来完成AJAX请求的处理,比如AjaxSyncHttpHandler
{
new AjaxProcHelper(this.p).Run();
}
核心处理类为AjaxProcHelper
这个类代码很多,但最重要的只有两行:
静态AJAX处理方法:
实例AJAX处理方法:
可以看到,是通过反射直接调用的指定方法.这也可以解释为什么即使是实例的AJAX方法,也获取不到控件的状态.因为它根本就没有去执行页面的生命周期.
5.ajax请求完成后,如果没有回调函数,则直接结束.如果有回调函数,则执行doStateChange方法,然后是createResponse方法,最后在endRequest方法中完成回调函数的调用.
以上就是一个完整的运用AjaxPro框架的AJAX请求过程.当然为了突出重点我省略了很多其它的要素.在查看这个框架的代码过程中,我感觉虽然我大致明白程序所表达的意思,但是有很多代码的编写方式让人难以理解.难道这就是传说中的混淆?
参考的文章: