DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> AJAX入門 >> AJAX詳解 >> Ajax是如何調用服務器端C#方法?
Ajax是如何調用服務器端C#方法?
編輯:AJAX詳解     

談起AJax做過web開發的都非常熟悉,就是通過xmlhttp request與服務器端通信而避免頁面刷新。關於Ajax是如何運作的,網上有很多帖子解釋其各JS文件的作用及調用xmlhttp的原理。但AJax到底是怎麼調用服務器端的C#代碼的呢?怎麼讓後台的方法運行並將結果反饋給XMLhttp的呢?曾經有個同事問起我這個問題,我還真懵了!本以為象.Net 1.1下通過form傳遞必要的EventName及EventPara等參數傳給服務器端繼而解析後執行對應的事件一樣來調用C#代碼的(.Net調用事件機制也不全是這麼回事,待探討),但通過仔細研究,發現原來遠不是這麼回事,而網上更深入的文章卻少之又少。

我們由淺到深吧,先看看相對表象的東西,即前台AJax相關的JavaScript代碼部分。之所以說相對膚淺和表象,是因為這些資料很多網友已經撰文解讀過。

凡要使用AJaxPro,我們大致要做以下工作:

1) 在項目中引用AjaxPro.dll(我用的是AJaxPro.2.dll,版本6.6.13.2),並在web.config中httpHandlers配置節添加:
<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AJaxPro.2"/>

2) 在要使用Ajax功能的頁面.cs文件上注冊AJax,例如:

        protected void Page_Load(object sender, EventArgs e)

        {

            // 注冊AJax

            AjaxPro.Utility.RegisterTypeForAJax(typeof(Default));

        }

3) 在.cs文件中聲明可以被AJax調用的函數(或屬性),如:

        [AjaxPro.AJaxMethod]

        public string GetChild(string parentId)

        {           

            return "return value from .cs file";

        }

4) 在.ASPx文件中用JavaScript調用AJax,如:

            <script language="Javascript">          

                    var items = DynLoadTree.Default.GetChild( "aa" ).value; // 通過AJax調用後台代碼

                    alert(items);

            </script>

做好以上四步,我們就基本實現了AJax頁面不刷新的功能了。那麼它是怎樣通過XMLhttp與服務器通訊的呢?運行後我們可以看到Html文件的源代碼多了幾行.ashx文件的引用:

<script type="text/Javascript" src="/AJaxpro/prototype.ashx"></script>
<script type="text/Javascript" src="/AJaxpro/core.ashx"></script>
<script type="text/Javascript" src="/AJaxpro/converter.ashx"></script>
<script type="text/Javascript" src="/AJaxpro/DynLoadTree.Default,DynLoadTree.ashx"></script>

   實際上這些.ashx就是在上面第2步AjaxPro.Utility.RegisterTypeForAjax注冊AJax時自動將這些引用添加到Html文檔輸出的。那這些文件是什麼文件呢?再看第1步中在web.config中添加到httpHandlers節中的配置,它告訴系統凡是收到ajaxpro路徑下已經ashx為後綴的請求就全部交給AjaxPro.AJaxHandlerFactory這個類來處理,而這些ashx經過處理後返回的就是一些JavaScript文件,和普通的JS引用沒有實質區別。

我們首先看看“DynLoadTree.Default,DynLoadTree.ashx”的內容:
if(typeof DynLoadTree == "undefined") DynLoadTree={};
DynLoadTree.Default_class = function() {};
Object.extend(DynLoadTree.Default_class.prototype, Object.extend(new AjaxPro.AJaxClass(), {   GetChild: function(parentId) {
return this.invoke("GetChild", {"parentId":parentId}, this.GetChild.getArguments().slice(1));
},url: '/AJaxpro/DynLoadTree.Default,DynLoadTree.ashx'}));
DynLoadTree.Default = new DynLoadTree.Default_class();

原來我們DynLoadTree.Default是在這裡定義的,而這個GetChild方法最終是調用“this.invoke("GetChild", {"parentId":parentId}, this.GetChild.getArguments().slice(1));”的,而invoke方法是在“core.ashx”中定義的。在core.ashx中定義了很多AJax核心的JS方法,例如Object.extand實現簡單的繼承(或閱擴展)。在invoke方法裡,首先是new了一個XMLHttp對象,然後重點做了幾件事:

this.XMLHttp.open("POST", this.url, async);
   this.XMLHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   this.XMLHttp.setRequestHeader("X-" + AJaxPro.ID + "-Method", method);
   this.XMLHttp.send(JSon);

xmlHttp.open說明了是向哪個服務器url發送請求,是同步請求還是異步請求。接下來就設置Content-Type的http header,然後再將method設置到http header中,以讓服務器端知道要調用什麼方法,最後send出去,同時參數JSon包含了調用這個方法所需的參數。至此,利用XMLhttp已經將請求發送給服務器了,接下來就等待服務器的反饋結果了(對於同步和異步不同的調用方式,對結果的處理是有區別的)。

 

但是,為什麼這樣一個請求給服務器後,服務器就自動調用制定的method呢?如果仔細一點,你可以發現xmlHttp.open裡的this.url到底是什麼?是要調用的頁面的地址麼?實際不是,這個this.url的值是“/AJaxpro/DynLoadTree.Default,DynLoadTree.ashx”。第一次看到這裡的時候,我很詫異,怎麼這個XMLhttp請求也發給一個ashx文件了呢?難道ashx文件不僅僅是用來動態生成JS文件的麼?同上,在web.config中已經配置了凡是ashx文件都交由類AjaxPro.AjaxHandlerFactory來處理,要想明白其中的奧秘,還得看看AjaxHandlerFactory裡到底都干了些什麼。為此,我用Reflector對AjaxPro.2.dll文件進行反編譯(我的資源裡提供下載),看了AJaxHandlerFactory的代碼才大徹大悟!

原來,在AJaxHandlerFactory的GetHandler方法裡是這麼寫的:

public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)

{

    ……

    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");

                ……

                default:                   

                    return new TypeJavaScriptHandler(type);

            }

        }

    }

    return null;

}

 

它首先對requestType進行判斷,如果是“GET”請求,則說明是Html裡對被引用的ashx文件的下載請求,則調用相應的Handler去生成對應的JavaScript內容輸出到客戶端;如果是“POST”請求,則說明是通過XMLHTTP發送過來的,是請求調用服務器端方法的,則返回相應的Handler利用反射機制調用請求的方法。

 

首先看看“GET”請求,對“GET”請求的處理很簡單,根據不同的文件名返回不同的Handler,對於“core”及“prototype”則返回EmbeddedJavaScriptHandler,對於“DynLoadTree.Default,DynLoadTree.ashx”則返回TypeJavascriptHandler。在EmbeddedJavaScriptHandler中,構造函數的參數表示要請求的是哪個文件,然後在ProcessRequest函數中提取指定的文件內容並輸出到客戶端,其實這些文件內容都是固定的,且已經放在資源裡的:

internal class EmbeddedJavaScriptHandler : IHttpHandler
{
    // FIElds
    private string fileName;     // Methods
    internal EmbeddedJavaScriptHandler(string fileName)
    {
        this.fileName = fileName;
    }     public void ProcessRequest(HttpContext context)
    {
       ……
        string[] strArray = this.fileName.Split(new char[] { ',' });
        Assembly executingAssembly = Assembly.GetExecutingAssembly();       
        for (int i = 0; i < strArray.Length; i++)
        {
            Stream manifestResourceStream = executingAssembly.GetManifestResourceStream("AJaxPro.2." + strArray[i] + ".JS");
            if (manifestResourceStream != null)
            {
                StreamReader reader = new StreamReader(manifestResourceStream);
                context.Response.Write(reader.ReadToEnd());
                context.Response.Write("\r\n");
                reader.Close();                if ((strArray[i] == "prototype") && Utility.Settings.OldStyle.Contains("objectExtendPrototype"))
                {
                    context.Response.Write("\r\nObject.prototype.extend = function(o, override) {\r\n\treturn Object.extend.apply(this, [this, o, override != false]);\r\n}\r\n");
                }
            }
        }
        ………
    }
   ……
}

   對於“DynLoadTree.Default,DynLoadTree.ashx”的請求,則交給TypeJavaScriptHandler處理:


internal class TypeJavaScriptHandler : IHttpHandler, IReadOnlySessionState, IRequiresSessionState

{

    // FIElds

    private Type type;

 

    // Methods

    internal TypeJavaScriptHandler(Type type);

    public void ProcessRequest(HttpContext context);

 

    // PropertIEs

    public bool IsReusable { get; }
}


   ProcessRequest會根據Type動態生成JavaScript內容並輸出到客戶端。   對於requestType是“POST”的請求,則返回相應的Handler進行處理。以AJaxSyncHttpHandler為例:

internal class AJaxSyncHttpHandler : IHttpHandler
{
    // FIElds
    private IAJaxProcessor p;     // Methods
    internal AjaxSyncHttpHandler(IAJaxProcessor p)
    {
        this.p = p;
    }     public void ProcessRequest(HttpContext context)
    {
        new AJaxProcHelper(this.p).Run();
    }     // PropertIEs
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
    其中ProcessRequest方法就就新建一個AjaxProcHelper對象,用該對象的Run方法來處理實質請求。可以簡略看看AJaxProcHelper.Run的代碼:

internal void Run()
{
    ……
    this.p.Context.Response.Expires = 0;
    this.p.Context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    this.p.Context.Response.ContentType = this.p.ContentType;
    this.p.Context.Response.ContentEncoding = Encoding.UTF8;
    ……
    object[] args = null;
    object o = null;
    args = this.p.RetreiveParameters();   
    string key = string.Concat(new object[] { this.p.Type.FullName, "|", this.p.GetType().Name, "|", this.p.AJaxMethod.Name, "|", this.p.GetHashCode() });
    if (this.p.Context.Cache[key] != null)
    {
        this.p.Context.Response.AddHeader("X-AJaxPro-Cache", "server");
        this.p.Context.Response.Write(this.p.Context.Cache[key]);
     }
     else
     {
         ……
         if (this.p.AJaxMethod.IsStatic)
         {
           o = this.p.Type.InvokeMember(this.p.AJaxMethod.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase, null, null, args);
         }
         else
         {
               ……
               object obj3 = Activator.CreateInstance(this.p.Type, new object[0]);
               o = this.p.AJaxMethod.Invoke(obj3, args);
         }
         ……                  
         if ((o != null) && (o.GetType() == typeof(XMLDocument)))
         {
             this.p.Context.Response.ContentType = "text/XML";
             ((XMLDocument) o).Save(this.p.Context.Response.OutputStream);
          }
        ……              
    }
}

    可以清晰的看到,Run中是通過反射機制調用相應的方法,再將結果寫入context輸出到客戶端的。
另外,我們也可以清晰的看到Utility中對RegisterTypeForAJax的幾個重載及實現方式:       
                     public static void RegisterTypeForAJax(Type type);
         public static void RegisterTypeForAJax(Type type, Page page);

同時,也可以看看AJaxMethodAttribute的定義(有關Attribute MSDN中有詳細的描述和實例):


[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
public class AJaxMethodAttribute : Attribute
{
    // FIElds
    private HttpSessionStateRequirement requireSessionState;
    private bool useAsyncProcessing;     // Methods
    public AJaxMethodAttribute();
    public AJaxMethodAttribute(HttpSessionStateRequirement requireSessionState);

    [Obsolete("The use of this argument is currently in beta state, please report any problems to bug@schwarz-interactive.de.")]
    public AJaxMethodAttribute(bool useAsyncProcessing);   

    [Obsolete("The recommended alternative is AjaxPro.AJaxServerCacheAttribute.", true)]
    public AJaxMethodAttribute(int cacheSeconds);

    [Obsolete("The recommended alternative is AjaxPro.AJaxNamespaceAttribute.", true)]
    public AJaxMethodAttribute(string methodName);


    [Obsolete("The use of this argument is currently in beta state, please report any problems to bug@schwarz-interactive.de.")]
    public AJaxMethodAttribute(HttpSessionStateRequirement requireSessionState, bool useAsyncProcessing);   

 

    [Obsolete("The recommended alternative is AjaxPro.AJaxServerCacheAttribute.", true)]
    public AJaxMethodAttribute(int cacheSeconds, HttpSessionStateRequirement requireSessionState);   

 

    [Obsolete("The recommended alternative for methodName is AjaxPro.AJaxNamespaceAttribute.", true)]
    public AJaxMethodAttribute(string methodName, HttpSessionStateRequirement requireSessionState);   

 

    [Obsolete("The recommended alternative for methodName is AjaxPro.AJaxNamespaceAttribute.", true)]
    public AJaxMethodAttribute(string methodName, int cacheSeconds);

 

    [Obsolete("The recommended alternative for methodName is AjaxPro.AJaxNamespaceAttribute.", true)]
    public AJaxMethodAttribute(string methodName, int cacheSeconds, HttpSessionStateRequirement requireSessionState);     // PropertIEs
   

    internal HttpSessionStateRequirement RequireSessionState { get; }
    internal bool UseAsyncProcessing { get; }
}

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved