Ajax框架原理分析之AjaxPro
最近工作比較閑,可以靜下心來研究自己感興趣的問題.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請求過程.當然為了突出重點我省略了很多其它的要素.在查看這個框架的代碼過程中,我感覺雖然我大致明白程序所表達的意思,但是有很多代碼的編寫方式讓人難以理解.難道這就是傳說中的混淆?
參考的文章:


浙公網安備 33010602011771號