亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

編寫輕量ajax組件02-AjaxPro淺析

2019-11-14 13:49:53
字體:
來源:轉載
供稿:網友

前言

  上一篇介紹了在webform平臺實現Ajax的一些方式,并且實現一個基類。這一篇我們來看一個開源的組件:ajaxPRo。雖然這是一個比較老的組件,不過實現思想和源碼還是值得我們學習的。通過上一篇的介紹,我們知道要調用頁面對象的方法,就是靠反射來實現的,關鍵是整個處理過程,包括反射調用方法、參數映射等。ajaxpro不僅在后臺幫我們實現了這個過程,在前臺也封裝了請求調用的方法,例如ajax的相關方法,用ajaxpro的方法就可以發送異步請求了,不需要自己封裝js或者使用js庫。接下來就對這個組件進行淺析。

一、ajaxpro的使用

  我們先來看這個組件如何使用。

  1. 注冊AjaxHandlerFactory

  在web.config里進行如下配置:

    <httpHandlers>      <add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/>    </httpHandlers>

  簡單的說,請求的url符合 ajaxpro/*.ashx 格式的,都會被AjaxHandlerFactory處理,這是一個實現IHandlerFactory接口的工廠類,用來獲取IHandler處理程序。其中type的格式是:"名稱控件.類名稱,程序集名稱"。

  2. 在頁面類Page_Load事件進行注冊

        protected void Page_Load(object sender, EventArgs e)        {            AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage));        }

  我們傳遞了本頁面對象的Type給ResisterTypoForAjax方法,這個方法用來在前臺注冊腳本,具體會調用當前Page對象的RegisterClientScriptBlock進行注冊,所以.aspx文件中必須有一個<form runat="server"></form>,否則腳本將無法注冊。(這里傳遞了Type,實際也可以做到不用傳遞的,內部通過HttpContext.Current.Handler.GetType().BaseType 也可以獲得這個類型)

  3.用AjaxMethod標記方法  

        [AjaxMethod]        public List<string> GetList(string input1,string input2)        {            return new List<string> { input1, input2 };        }

  AjaxMethod是一個標記屬性,表示這個方法用于處理ajax請求,它最終通過反射執行;它有幾個構造函數對,對于有些需要緩存的數據,可以設置緩存時間;如果我們的請求不需要使用session,可以設置HttpSessionStateRequirement;如果請求需要異步,例如請求一個耗時的web服務,也可以設置處理程序為異步狀態。

  方法的返回值可以是簡單的類型,也可以是復雜的類型;例如集合類型在前臺獲得就是一個數組。

  4.前臺調用

  后臺的配置和使用都非常簡單,接下來我們看前臺如何發起請求。

    function GetList() {        //var result = AjaxProNamespace.AjaxProPage.GetList("a", "b").value;        //console.log(result);        AjaxProNamespace.AjaxProPage.GetList("a", "b", function (result) {            console.log(result);        });           }

  這里AjaxProNamespace 是頁面類所在的名稱空間,AjaxProPage 就是頁面類的名稱,GetList是標記的方法。為什么可以這樣寫呢?前面說到,ajaxpro會在前臺注冊腳本,它會根據我們頁面對象的相關信息生成如下腳本,所以我們才可以這樣調用,而完全不用自己寫js或者用jquery庫的方法。

if(typeof AjaxProNamespace == "undefined") AjaxProNamespace={};if(typeof AjaxProNamespace.AjaxProPage_class == "undefined") AjaxProNamespace.AjaxProPage_class={};AjaxProNamespace.AjaxProPage_class = function() {};Object.extend(AjaxProNamespace.AjaxProPage_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {	GetList: function(input1, input2) {		return this.invoke("GetList", {"input1":input1, "input2":input2}, this.GetList.getArguments().slice(2));	},	url: '/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx'}));AjaxProNamespace.AjaxProPage = new AjaxProNamespace.AjaxProPage_class();

  GetList的參數對應后臺方法的參數,類型必須可以轉換,否則調用會失敗。最后一個參數為回調函數,回調函數的參數是對返回結果進行封裝的對象,其value屬性就是執行成功返回的值,如上面返回的就是一個數組對象。其error包括了失敗的信息。

  注意,上面注釋掉的部分是同步請求的做法,這往往不是我們想要的,我曾經就見過有人這樣錯誤的使用。

二、ajaxpro處理請求原理

  這里主要關注組件處理ajax請求的過程,其它輔助功能不做介紹。

  1.生成輔助腳本

  在Page_Load事件里我們調用了AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage)); 用來注冊所需要的腳本。我們注意到在前臺頁面引入了如下腳本:

也就是每個頁面都會都會發起這幾個Get 請求。這幾個都是.ashx結尾的文件,但實際里面都是js代碼;這些js有的是作為資源嵌套在dll內部,有的是自動生成的,主要是封裝了ajax請求相關方法,以及讓我們可以用:名稱空間.頁面類名稱.標記方法名稱 這樣去調用方法。為什么要用.ashx而不是用.js呢?因為作為組件內部的資源文件,外部無法直接請求.js文件,而.ashx可以被攔截,然后用Response.Write將內容輸出。

  如果每次都生成和發送這些腳本的效率是很低的,ajaxpro內部的處理是判斷請求頭的If-None-Math和If-Modified-Since,如果兩個都和緩存的一樣,就返回一個304狀態碼。所以,客戶端只有首次請求服務端會返回文件的內容,后續的都只返回304表示使用本地緩存。我們刷新頁面可以驗證這個過程:

  我們知道304狀態碼表示服務端告訴瀏覽器可以使用本地緩存,它的具體過程是這樣的:瀏覽器將發送請求,Request包括If-None-Math和If-Modified-Since;服務端接收到請求后,判斷If-None-Math和ETag是否一樣,判斷If-Modified-Since和請求內容的Last-Modified-Time是否一樣;如果都一樣,則返回304狀態碼,瀏覽器接收到304,就直接使用本地緩存;如果有一個不一樣,服務端都將輸出具體內容,此時Response包含新的ETag和Last-Modified-Time。這個過程最明顯的好處就是服務端不需要發送內容給瀏覽器,但缺點就是瀏覽器和服務端還需要一次請求-響應的過程。個人認為這里可以使用Cache-Control,并設置一個較大值的時間,因為這里的js文件內容基本是不會變化的。Cache-Control表示瀏覽器請求時,先判斷請求是否過時,如果沒有過時,則直接從本地緩存獲得,這個過程瀏覽器不需要和服務端建立任何請求;如果過時,瀏覽器才會發起請求。(需要注意的是,瀏覽器緩存都是基于Get請求的,Post請求是不會被緩存的)

  2. 攔截請求

  HttpHandler(IHttpHandler) 和 HttpModule(IHttpModule) 是asp.net 兩個重要的組件,讓我們可以在asp.net的基礎上很方便的進行擴展。HttpHandler對應某種具體的請求,例如.ashx,.aspx等;HttpModule是一個攔截器,可以在管道的某個事件對所有請求進行攔截。簡單的說,在管道中,Httpapplication會觸發一系列事件,我們在通過HttpModule對某個事件進行注冊,例如我們可以在處理程序對象生成前攔截請求,然后映射到自己的處理程序;而實際處理請求返回結果的是HttpHandler,例如Page用來生成html。

  以asp.net mvc框架為例,它是建立在asp.net 路由機制的基礎上的,asp.net 路由系統通過一個UrlRoutingModule對請求進行攔截,具體是在PostResolveRequestCache事件進行攔截,對url進行解析,封裝相應的路由數據后,最終將請求交給一個MvcHandler進行處理,MvcHandler實現了IHttpHandler接口。

  前面我們進行了如下配置:<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/> 這表明了任何的以 ajaxpro/任意名稱.ashx結尾的 Post/Get 請求,都交給AjaxPro.AjaxHandlerFactory進行處理,它是一個實現了IHandlerFactory的處理程序工廠,用來生成具體的IHttpHandler。組件內部定義了多個實現IHttpHandler的類,有的是為了生成js腳本的,對于處理ajax請求,主要分為兩類:異步(IHttpAsyncHandler)和非異步(IHttpHandler);在這兩類的基礎上,對于Session的狀態的支持又分為三種:支持讀寫(實現IRequiresSessionState標記接口)的Handler、只讀(實現IReadOnlySessionState標記接口)的Handler和不支持Session的Handler。具體生成什么樣的Handler是通過AjaxMethod進行判斷的。

  IHttpHandler的ProcessRequest(異步就是BeginProcessRequest)就用來執行請求返回輸出結果的。如果只需要一種處理程序我們也可以實現IHttpHandler。IHandlerFactory的定義如下:

    public interface IHttpHandlerFactory    {        IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);        void ReleaseHandler(IHttpHandler handler);    }  

  所以,ajaxpro的所有請求都會符合ajaxpro/*.ashx格式,然后在GetHandler方法,就可以進行具體的處理,返回結果是IHttpHandler;以非異步狀態為例,如果我們配置了需要Session,就會生成一個實現IHttpHandler和IRequiresSessionState的Handler,如果需要只讀的Session,就會生成一個實現IHttpHandler和IReadOnlySessionState的Handler;這些信息可以通過反射從AjaxMethod標記屬性獲得。AjaxHandlerFactory的主要代碼如下:

        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)        {            string filename = Path.GetFileNameWithoutExtension(context.Request.Path);            Type t = null;            Exception typeException = null;            bool isInTypesList = false;            switch (requestType)            {                //Get請求,獲取前面的那4個腳本                case "GET":		                    switch (filename.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:                            return new TypeJavaScriptHandler(t);                    }                case "POST":                    IAjaxProcessor[] p = new IAjaxProcessor[2];                    p[0] = new xmlHttpRequestProcessor(context, t);                    p[1] = new IFrameProcessor(context, t);                    for (int i = 0; i < p.Length; i++)                    {                        if (p[i].CanHandleRequest)                        {                            //獲取標記方法的AjaxMethod屬性                            AjaxMethodAttribute[] ma = (AjaxMethodAttribute[])p[i].AjaxMethod.GetCustomAttributes(typeof(AjaxMethodAttribute), true);                            bool useAsync = false;                            HttpSessionStateRequirement sessionReq = HttpSessionStateRequirement.ReadWrite;                            if (ma.Length > 0)                            {                                useAsync = ma[0].UseAsyncProcessing;                                if (ma[0].RequireSessionState != HttpSessionStateRequirement.UseDefault)                                    sessionReq = ma[0].RequireSessionState;                            }                            //6種Handler,根據是否異步,session狀態返回指定的Handler                            switch (sessionReq)                            {                                case HttpSessionStateRequirement.Read:                                    if (!useAsync)                                        return new AjaxSyncHttpHandlerSessionReadOnly(p[i]);                                    else                                        return new AjaxAsyncHttpHandlerSessionReadOnly(p[i]);                                case HttpSessionStateRequirement.ReadWrite:                                    if (!useAsync)                                        return new AjaxSyncHttpHandlerSession(p[i]);                                    else                                        return new AjaxAsyncHttpHandlerSession(p[i]);                                case HttpSessionStateRequirement.None:                                    if (!useAsync)                                        return new AjaxSyncHttpHandler(p[i]);                                    else                                        return new AjaxAsyncHttpHandler(p[i]);                                default:                                    if (!useAsync)                                        return new AjaxSyncHttpHandlerSession(p[i]);                                    else                                        return new AjaxAsyncHttpHandlerSession(p[i]);                            }                        }                    }                    break;            }            return null;        }

  3. 反射執行方法

  當獲得一個處理本次請求的Handler后,就可以在其ProcessRequest(異步為BeginProcessRequest)執行指定的方法。要執行一個頁面對象的方法,我們必須知道指定頁面所在的程序集,名稱空間,頁面類的名稱以及方法的名稱。這似乎符合我們前面:名稱空間.類名稱.方法名稱的調用方式。為了與一般請求區分開,讓組件具有足夠的獨立性,ajaxpro只攔截符合"ajaxpro/*.ashx格式的請求,這說明我們的ajax請求也要符合這個格式。如:http://localhost:50712/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx,這個格式由前臺腳本自動生成,并不需要我們去構造。仔細觀察,會發現AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode 就是頁面類的完全限定名:名稱空間.類名稱,程序集名稱,通過這個我們就可以生成具體的Type,然后進行反射獲取信息。那么方法的名稱呢?ajaxpro將其放在http header 中,名稱為:X-AjaxPro-Method。有了這些信息,就可以反射執行方法了。這里核心代碼為:

        internal void Run()        {            try            {                //設置輸出結果不緩存(這不一定是我們想要的)                p.Context.Response.Expires = 0;                p.Context.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);                p.Context.Response.ContentType = p.ContentType;                p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;                //驗證ajax請求                if (!p.IsValidAjaxToken())                {                    p.SerializeObject(new System.Security.SecurityException("The AjaxPro-Token is not valid."));                    return;                }                //方法參數對象數組                object[] po = null;                //請求處理結果                object res = null;                try                {                    //獲取參數                    po = p.RetreiveParameters();                }                catch (Exception ex){}                //獲取緩存的Key                string cacheKey = p.Type.FullName + "|" + p.GetType().Name + "|" + p.AjaxMethod.Name + "|" + p.GetHashCode();                if (p.Context.Cache[cacheKey] != null)                {                    //如果緩存存在,則直接使用緩存                    p.Context.Response.AddHeader("X-" + Constant.AjaxID + "-Cache", "server");                    p.Context.Response.Write(p.Context.Cache[cacheKey]);                    return;                }                try                {                    if (p.AjaxMethod.IsStatic)                    {                        //使用反射調用靜態方法                        try                        {                            res = p.Type.InvokeMember(                                p.AjaxMethod.Name,                                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.InvokeMethod,                                null, null, po);                        }                        catch (Exception ex){}                    }                    else                    {                        try                        {                            //創建實例對象,反射調用實例方法                            object c = (object)Activator.CreateInstance(p.Type, new object[] { });                            if (c != null)                            {                                res = p.AjaxMethod.Invoke(c, po);                            }                        }                        catch (Exception ex){}                    }                }                catch (Exception ex){}                try                {                    //判斷結果是不是xml,如是設置ContentType                    if (res != null && res.GetType() == typeof(System.Xml.XmlDocument))                    {                        p.Context.Response.ContentType = "text/xml";                        p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;                        ((System.Xml.XmlDocument)res).Save(p.Context.Response.OutputStream);                        return;                    }                    string result = null; ;                    System.Text.StringBuilder sb = new System.Text.StringBuilder();                    try                    {                        result = p.SerializeObject(res);                    }                    catch (Exception ex){}                    //如果需要緩存,則將結果寫入緩存                    if (p.ServerCacheAttributes.Length > 0)                    {                        if (p.ServerCacheAttributes[0].IsCacheEnabled)                        {                            p.Context.Cache.Add(cacheKey, result, null, DateTime.Now.Add(p.ServerCacheAttributes[0].CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);                        }                    }                }                catch (Exception ex){}            }            catch (Exception ex){}        }

三、總結

  我們總結一下ajaxpro的核心處理流程,它通過一個IHttpHandlerFactory攔截指定格式的url,然后從中獲取類型的完全限定名生成類型對象,接著通過反射獲取標記方法的特性,生成一個自定義的實現IHttpHandler接口的對象;在其ProcessRequest方法中,從http headers獲取方法名稱,通過反射進行參數映射并執行函數。

  ajaxpro 具有如下優點:

  1. 配置簡單。

  2. 可以配合其它組件一起使用。

  3. 封裝前臺腳本,我們不用自己封裝或者使用其它腳本庫。

  4. 對返回值處理,我們可以返回簡單類型或者復雜類型都會自動序列化?! ?/p>

  缺點是:

  1. 頁面會多出4個請求。盡管會利用304緩存,但還是需要一次請求-響應的過程。

  2. ajax無法使用Get請求。由于自定義了url格式,使用這種格式就無法用Get請求了,我們知道Get請求是可以被瀏覽器緩存的,雅虎前端優化建議中有一條就是多用get請求。事實上,應該把名稱空間.類名稱,程序集放到http header中,然后提供了一個type類型的參數讓我們自由選擇。

  3. 與<form runat="server">綁定。目的是用了為我們生成前臺腳本,但如果我們希望用.html文件 + .aspx.cs 的方式就不能用了(博客園有些頁面就用了這種方式);甚至我們的接口可能要給移動端使用,這種方便就變成了限制。

  4. 反射。這樣效率是比較低的,它甚至沒有像我們之前的頁面類一樣,對MethodInfo進行緩存。

  可以看出,如果在不太計較效率的情況,這個組件還是值得使用的。這里只是做一個核心的介紹,里面還有很多其它功能,這是ajaxpro組件的源代碼,有興趣的朋友可以研究研究。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久久久久久久久久久久久久久久av| 欧美精品在线免费| 欧美激情视频给我| 欧美日韩在线影院| 97在线日本国产| 日韩精品中文字幕久久臀| 久久久亚洲影院| 欧美精品第一页在线播放| 亚洲精品美女在线| 国产精品视频资源| 欧美麻豆久久久久久中文| 日韩电影中文字幕一区| 日韩有码在线观看| 亚洲欧美三级在线| 国产精品久久久久久av下载红粉| 欧美另类老女人| 最近2019中文字幕一页二页| 亚洲精品色婷婷福利天堂| 一本色道久久综合狠狠躁篇的优点| 日韩综合中文字幕| 亚洲精品有码在线| 欧美一级大胆视频| 亚洲精品中文字幕女同| 91av在线不卡| 亚洲xxxxx性| 欧美在线播放视频| 精品久久久久久中文字幕| 国产日韩在线观看av| 欧美在线国产精品| 国产亚洲欧美日韩美女| 97视频网站入口| 久久中文字幕在线视频| 91精品在线观看视频| 欧美精品久久久久久久久| 国内精品伊人久久| 国产精品久久999| 亚洲精品国偷自产在线99热| 日韩亚洲欧美中文在线| 中文字幕亚洲一区在线观看| 日韩精品一区二区三区第95| 亚洲人a成www在线影院| 亚洲午夜小视频| 亚洲专区国产精品| 国产亚洲欧洲高清| 久久精品这里热有精品| 亚洲福利视频在线| 国产精品综合不卡av| 国内外成人免费激情在线视频网站| 成人高清视频观看www| 亚洲一区二区久久久久久久| 国产98色在线| 日韩精品极品在线观看| 亚洲在线视频福利| 成人精品久久av网站| 91久久在线视频| 久久精品成人一区二区三区| 91免费精品视频| 欧美激情亚洲国产| 亚洲人在线观看| 久久久久久久久国产精品| 26uuu另类亚洲欧美日本一| 久久久国产视频| 青草青草久热精品视频在线观看| 亚洲国产精品成人精品| 久久99精品久久久久久琪琪| 日韩精品视频在线观看网址| 成人免费视频网址| 国产亚洲欧美日韩精品| 亚洲成av人影院在线观看| 久久久精品一区二区| 欧美大秀在线观看| 一区二区三区四区精品| 欧美激情国产精品| 欧美裸体视频网站| 日韩视频免费中文字幕| 国产精品亚洲片夜色在线| 午夜精品国产精品大乳美女| 欧美专区第一页| 中文字幕亚洲在线| 亚洲精品一区av在线播放| 欧美猛交ⅹxxx乱大交视频| 精品日本美女福利在线观看| 在线视频日本亚洲性| 成人激情视频在线观看| 欧美高清视频一区二区| 国产成人小视频在线观看| 久久99亚洲精品| 久久深夜福利免费观看| 国产精品私拍pans大尺度在线| 国产精品久久97| 欧美日韩一区二区三区| 亚洲欧美成人网| 欧美性生活大片免费观看网址| 成人网址在线观看| 国产一区二区三区在线观看网站| 日韩在线视频导航| 亚洲精品日产aⅴ| 亚洲精品视频在线播放| 最好看的2019的中文字幕视频| 高清欧美一区二区三区| 欧美成人精品xxx| 国产精品久久中文| 中文字幕精品—区二区| 日韩成人xxxx| 日韩av男人的天堂| 亚洲免费视频网站| 欧美日韩国产区| 欧美日韩激情网| 九九久久久久99精品| 免费91在线视频| 日韩中文视频免费在线观看| 国内自拍欧美激情| 538国产精品一区二区免费视频| 亚洲精品mp4| 欧美又大又粗又长| 国产精品国产三级国产专播精品人| 中文字幕在线看视频国产欧美| 97视频在线观看亚洲| 国产主播喷水一区二区| 欧美性猛交视频| 欧美性极品xxxx做受| 欧美激情videos| 久久综合伊人77777蜜臀| 欧美成人午夜激情在线| 久久久成人的性感天堂| 成人福利视频在线观看| 三级精品视频久久久久| 久久男人的天堂| 激情亚洲一区二区三区四区| 欧美日韩在线观看视频小说| 国产视频精品免费播放| 美女视频黄免费的亚洲男人天堂| 亚洲色图狂野欧美| 亚洲国语精品自产拍在线观看| 亚洲专区国产精品| 国产精品久久视频| 日韩免费不卡av| 国产精品久久久一区| 欧美日韩国产成人在线| 色偷偷91综合久久噜噜| 亚洲欧美日韩国产成人| 欧美亚洲另类激情另类| 欧美成人午夜激情| 欧美中文在线视频| 亚洲综合日韩在线| 色在人av网站天堂精品| 中文字幕亚洲情99在线| 日本一本a高清免费不卡| 欧美成在线视频| 日韩成人黄色av| 亚洲欧美一区二区三区情侣bbw| 三级精品视频久久久久| 57pao国产成人免费| 日韩一区二区在线视频| 午夜免费在线观看精品视频| 欧美疯狂xxxx大交乱88av| 欧美性感美女h网站在线观看免费| 亚洲国产成人一区| 亚洲国产婷婷香蕉久久久久久| 欧美日韩激情视频8区| 久久久久久久久久婷婷| 国产91精品黑色丝袜高跟鞋| 久久精品国产69国产精品亚洲| 91色视频在线导航|