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

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

編寫輕量ajax組件01-對比webform平臺上的各種實現方式

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

前言

  asp.net WebForm 和 Asp.net MVC(簡稱MVC) 都是基于Asp.net的web開發框架,兩者有很大的區別,其中一個就是MVC更加注重http本質,而WebForm試圖屏蔽http,為此提供了大量的服務器控件和ViewState機制,讓開發人員可以像開發Windows Form應用程序一樣,基于事件模型去編程。兩者各有優缺點和適用情景,但MVC現在是許多Asp.net開發者的首選。

  WebForm是建立在Asp.net的基礎上的,Asp.net提供了足夠的擴展性,我們也可以利用這些在WebForm下編寫像MVC一樣的框架,這個有機會再寫。說到WebForm很多人就會聯想到服務器控件(拖控件?。。。?,其實不然,我們也可以完全不使用服務器控件,像MVC那樣關注html。WebForm要拋棄服務器控件,集中關注html,首先就要將<form runat="server"></form>標簽去掉,這個runat server 的form 是其PostBack機制的基礎。既然我們要回歸到html+CSS+js,那么意味著許多東西都要自己實現,例如處理Ajax請求。不像MVC那樣,WebForm開始的設計就將服務器控件作為主要組成部分,如果不使用它,那么只能利用它的擴展性去實現。

  本系列就是實現一個基于WebForm平臺的輕量級ajax組件,主要分為三個部分:

  1. 介紹WebForm下各種實現方式。

  2. 分析ajaxPRo組件。

  3. 編寫自己的ajax組件。

一、Ajax簡介

  異步允許我們在不刷新整個頁面的情況下,像服務器請求或提交數據。對于復雜的頁面,為了請求一點數據而重載整個頁面顯然是很低效的,ajax就是為了解決這個問題的。ajax的核心是xmlHttpRequest對象,通過該對象,以文本的形式向服務器提交請求。xmlhttpRequest2.0后,還支持提交二進制數據。

  ajax安全:出于安全考慮,ajax受同源策略限制;也就是只能訪問同一個域、同一個端口的請求,跨域請求會被拒絕。當然有時候需求需要跨域發送請求,常用的跨域處理方法有CORS(跨域資源共享)和JSONP(參數式JSON)。

  ajax數據交互格式:雖然Ajax核心對象XmlHttpRequest有"XML"字眼,但客戶端與服務器數據交換格式不局限于xml,例如現在更多是使用json格式。  

  ajax 也是有缺點的。例如對搜索引擎的支持不太好;有時候也會違背url資源定位的初衷。

二、Asp.net MVC 平臺下使用ajax

  在MVC里,ajax調用后臺方法非常方便,只需要指定Action的名稱即可。

  前臺代碼:

<body>    <h1>index</h1>    <input type="button" value="GetData" onclick="getData()" />    <span id="result"></span></body><script type="text/javascript">    function getData() {        $.get("GetData", function (data) {            $("#result").text(data);        });    }</script>

  后臺代碼:

     public class AjaxController : Controller    {        public ActionResult GetData()        {            if(Request.IsAjaxRequest())            {                return Content("data");            }            return View();        }    }

三、WebForm 平臺下使用ajax

  3.1 基于服務器控件包或者第三方組件

  這是基于服務器控件的,例如ajax toolkit工具包,或者像FineUI這樣的組件。web前端始終是由html+css+js組成的,只不過如何去生成的問題。原生的我們可以自己編寫,或者用一些前端插件;基于服務器控件的,都是在后臺生成的,通常效率也低一點。服務器組件會在前臺生成一系列代理,本質還是一樣的,只不過控件封裝了這個過程,不需要我們自己編寫?;诳丶蛘叩谌浇M件的模式,在一些管理系統還是挺有用的,訪問量不是很大,可以快速開發。

  3.2 基于ICallbackEventHandler接口

  .net 提供了ICallbackEventHandler接口,用于處理回調請求。該接口需要用ClientScriptManager在前臺生成代理腳本,用于發送和接收請求,所以需要<form runat="server">標簽。

  前臺代碼:

<body>    <form id="form1" runat="server">    <div>                <input type="button" value="獲取回調結果" onclick="callServer()" />        <span id="result" style="color:Red;"></span>    </div>    </form></body><script type="text/Javascript">    function getCallbackResult(result){        document.getElementById("result").innerHTML = result;    }</script>

  后臺代碼:

    public partial class Test1 : System.Web.UI.Page, ICallbackEventHandler    {                protected void Page_Load(object sender, EventArgs e)        {            //客戶端腳本Manager            ClientScriptManager scriptMgr = this.ClientScript;            //獲取回調函數,getCallbackResult就是回調函數            string functionName = scriptMgr.GetCallbackEventReference(this, "", "getCallbackResult", "");            //發起請求的腳本,callServer就是點擊按鈕事件的執行函數            string scriptExecutor = "function callServer(){" + functionName + ";}";            //注冊腳本            scriptMgr.RegisterClientScriptBlock(this.GetType(), "callServer", scriptExecutor, true);        }        //接口方法        public string GetCallbackResult()        {            return "callback result";        }        //接口方法        public void RaiseCallbackEvent(string eventArgument)        {        }    }

  這種方式有以下缺點:

  1. 實現起來較復雜,每個頁面Load事件都要去注冊相應的腳本。

  2. 前臺會生成一個用于代理的腳本文件。

  3. 對于頁面交互復雜的,實現起來非常麻煩。

  4. 雖然是回調,但是此時頁面對象還是生成了。

  3.3 使用一般處理程序

  一般處理程序其實是一個實現了IHttpHandler接口類,與頁面類一樣,它也可以用于處理請求。一般處理程序通常不用于生成html,也沒有復雜的事件機制,只有一個ProcessRequest入口用于處理請求。我們可以將ajax請求地址寫成.ashx文件的路徑,這樣就可以處理了,而且效率比較高。

  要輸出文本內容只需要Response.Write(data)即可,例如,從數據庫獲取數據后,序列化為json格式字符串,然后輸出。前面說到,一般處理程序不像頁面一樣原來生成html,如果要生成html,可以通過加載用戶控件生成。如:

        public void ProcessRequest(HttpContext context)        {            Page page = new Page();            Control control = page.LoadControl("~/PageOrAshx/UserInfo.ascx");            if (control != null)            {                StringWriter sw = new StringWriter();                HtmlTextWriter writer = new HtmlTextWriter(sw);                control.RenderControl(writer);                string html = sw.ToString();                context.Response.Write(html);                            }        }

  這種方式的優點是輕量、高效;缺點是對于交互多的需要定義許多ashx文件,加大了管理和維護成本。

  3.4 頁面基類

  將處理ajax請求的方法定義在頁面對象內,這樣每個頁面就可以專注處理本頁面相關的請求了。這里有點需要注意。

  1.如何知道這個請求是ajax請求?

    通過請求X-Requested-With:XMLHttlRequest 可以判斷,大部份瀏覽器的異步請求都會包含這個請求頭;也可以通過自定義請求頭實現,例如:AjaxFlag:XHR。

  2.在哪里統一處理?

    如果在每個頁面類里判斷和調用是很麻煩的,所以將這個處理過程轉到一個頁面基類里處理。

  3.如何知道調用的是哪個方法?

    通過傳參或者定義在請求頭都可以,例如:MethodName:GetData。

  4.知道方法名稱了,如何動態調用?

    反射。

  5.如何知道該方法可以被外部調用?

    可以認為public類型的就可以被外部調用,也可以通過標記屬性標記。

  通過上面的分析,簡單實現如下  

  頁面基類:

    public class PageBase : Page    {        public override void ProcessRequest(HttpContext context)        {            HttpRequest request = context.Request;            if (string.Compare(request.Headers["AjaxFlag"],"AjaxFlag",0) == 0)            {                string methodName = request.Headers["MethodName"];                if (string.IsNullOrEmpty(methodName))                {                    EndRequest("MethodName標記不能為空!");                }                Type type = this.GetType().BaseType;                MethodInfo info = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);                if (info == null)                {                    EndRequest("找不到合適的方法調用!");                }                                string data = info.Invoke(this, null) as string;                EndRequest(data);            }            base.ProcessRequest(context);        }        private void EndRequest(string msg)        {            HttpResponse response = this.Context.Response;            response.Write(msg);            response.End();        }    }

  頁面類:

    public partial class Test1 : PageBase    {        protected void Page_Load(object sender, EventArgs e)        {        }        public string GetData()        {            return "213";        }    }  

  前臺代碼:

    function getData(){        $.ajax({            headers:{"AjaxFlag":"XHR","MethodName":"GetData"},            success:function(data){                $("#result").text(data);            }        });    }

四、優化版頁面基類

  上面的頁面基類功能很少,而且通過反射這樣調用的效率很低。這里優化一下:

  1.可以支持簡單類型的參數。

    例如上面的GetData可以是:GetData(string name),通過函數元數據可以獲取相關的參數,再根據請求的參數,就可以設置參數了。

  2.加入標記屬性。

    只有被AjaxMethodAttribute標記的屬性才能被外部調用。

  3.優化反射。

    利用緩存,避免每次都根據函數名稱去搜索函數信息。

  標記屬性:

    public class AjaxMethodAttribute : Attribute    {    }

  緩存對象:  

    public class CacheMethodInfo    {        public string MethodName { get; set; }        public MethodInfo MethodInfo { get; set; }        public ParameterInfo[] Parameters { get; set; }    }

  基類代碼:

     public class PageBase : Page    {        private static Hashtable _ajaxTable = Hashtable.Synchronized(new Hashtable());        public override void ProcessRequest(HttpContext context)        {                        HttpRequest request = context.Request;            if (string.Compare(request.Headers["AjaxFlag"],"XHR",true) == 0)            {                InvokeMethod(request.Headers["MethodName"]);            }            base.ProcessRequest(context);        }        /// <summary>        /// 反射執行函數        /// </summary>        /// <param name="methodName"></param>        private void InvokeMethod(string methodName)        {            if (string.IsNullOrEmpty(methodName))            {                EndRequest("MethodName標記不能為空!");            }            CacheMethodInfo targetInfo = TryGetMethodInfo(methodName);            if (targetInfo == null)            {                EndRequest("找不到合適的方法調用!");            }            try            {                object[] parameters = GetParameters(targetInfo.Parameters);                string data = targetInfo.MethodInfo.Invoke(this, parameters) as string;                EndRequest(data);            }            catch (FormatException)            {                EndRequest("參數類型匹配發生錯誤!");            }            catch (InvalidCastException)            {                EndRequest("參數類型轉換發生錯誤!");            }            catch (ThreadAbortException)            {            }            catch (Exception e)            {                EndRequest(e.Message);            }        }        /// <summary>        /// 獲取函數元數據并緩存        /// </summary>        /// <param name="methodName"></param>        /// <returns></returns>        private CacheMethodInfo TryGetMethodInfo(string methodName)        {            Type type = this.GetType().BaseType;            string cacheKey = type.AssemblyQualifiedName;            Dictionary<string, CacheMethodInfo> dic = _ajaxTable[cacheKey] as Dictionary<string, CacheMethodInfo>;            if (dic == null)            {                dic = new Dictionary<string, CacheMethodInfo>();                MethodInfo[] methodInfos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)                                            let ma = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false)                                            where ma.Length > 0                                            select m).ToArray();                foreach (var mi in methodInfos)                {                    CacheMethodInfo cacheInfo = new CacheMethodInfo();                    cacheInfo.MethodName = mi.Name;                    cacheInfo.MethodInfo = mi;                    cacheInfo.Parameters = mi.GetParameters();                    dic.Add(mi.Name, cacheInfo);                }                _ajaxTable.Add(cacheKey, dic);            }            CacheMethodInfo targetInfo = null;            dic.TryGetValue(methodName, out targetInfo);            return targetInfo;        }        /// <summary>        /// 獲取函數參數        /// </summary>        /// <param name="parameterInfos"></param>        /// <returns></returns>        private object[] GetParameters(ParameterInfo[] parameterInfos)        {            if (parameterInfos == null || parameterInfos.Length <= 0)            {                return null;            }            HttpRequest request = this.Context.Request;            NameValueCollection nvc = null;            string requestType = request.RequestType;            if (string.Compare("GET", requestType, true) == 0)            {                nvc = request.QueryString;            }            else            {                nvc = request.Form;            }            int length = parameterInfos.Length;            object[] parameters = new object[length];            if (nvc == null || nvc.Count <= 0)            {                return parameters;            }            for (int i = 0; i < length; i++)            {                ParameterInfo pi = parameterInfos[i];                string[] values = nvc.GetValues(pi.Name);                object value = null;                if (values != null)                {                    if (values.Length > 1)                    {                        value = String.Join(",", values);                    }                    else                    {                        value = values[0];                    }                }                if (value == null)                {                    continue;                }                parameters[i] = Convert.ChangeType(value, pi.ParameterType);            }                        return parameters;        }        private void EndRequest(string msg)        {            HttpResponse response = this.Context.Response;            response.Write(msg);            response.End();        }    }

  頁面類:

        [AjaxMethod]        public string GetData3(int i, double d, string str)        {            string[] datas = new string[] { i.ToString(), d.ToString(), str };            return "參數分別是:" + String.Join(",", datas);        }    

  前臺代碼:

    function getData3(){        $.ajax({            headers:{"AjaxFlag":"XHR","MethodName":"GetData3"},            data:{"i":1,"d":"10.1a","str":"hehe"},            success:function(data){                $("#result").text(data);            }        });    }

五、總結

  上面的頁面基類已經具備可以完成基本的功能,但它還不夠好。主要有:

  1. 依附在頁面基類。對于本來有頁面基類的,無疑會變得更加復雜。我們希望把它獨立開來,變成一個單獨的組件。

  2. 效率問題。反射的效率是很低的,尤其在web這類應用程序上,更應該慎用。以動態執行函數為例,效率主要低在:a.根據字符串動態查找函數的過程。b.執行函數時,反射內部需要將參數打包成一個數組,再將參數解析到線程棧上;在調用前CLR還要檢測參數的正確性,再判斷有沒有權限執行。上面的優化其實只優化了一半,也就是優化了查找的過程,而Invoke同樣會有性能損失。當然,隨著.net版本越高,反射的效率也會有所提升,但這種動態的東西,始終是用效率換取靈活性的。

  3.不能支持復雜參數。有時候參數比較多,函數參數一般會封裝成一個對象類型。

  4. AjaxMethodAttribute只是一個空的標記屬性。我們可以為它加入一些功能,例如,標記函數的名稱、是否使用session、緩存設置等都可以再這里完成。

  用過WebForm的朋友可能會提到AjaxPro組件,這是一個開源的組件,下一篇就通過源碼了解這個組件,借鑒它的處理過程,并且分析它的優缺點。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久av红桃一区二区小说| 高跟丝袜欧美一区| 国产日韩欧美自拍| 成人免费观看a| 亚洲国产精品久久精品怡红院| 国产suv精品一区二区三区88区| 精品国产欧美一区二区五十路| 国产在线精品一区免费香蕉| 亚洲人成自拍网站| 欧美日韩午夜视频在线观看| 九九综合九九综合| 成人黄色影片在线| 亚洲国产精久久久久久| 午夜精品久久久久久久99热浪潮| 中日韩美女免费视频网址在线观看| 亚洲欧美在线一区| 日韩av影院在线观看| 一区二区亚洲欧洲国产日韩| 中文字幕亚洲情99在线| 亚洲成人激情小说| 色妞色视频一区二区三区四区| 国产亚洲欧洲在线| 亚洲精品色婷婷福利天堂| 日本免费一区二区三区视频观看| 色综合久久悠悠| 亚洲国产精品人人爽夜夜爽| 成人av色在线观看| 国内精品模特av私拍在线观看| 欧美精品在线观看| 国产不卡在线观看| 久久久精品欧美| 性欧美xxxx| 国产精品 欧美在线| 亚洲综合av影视| 国产精品情侣自拍| 久久亚洲春色中文字幕| 欧美另类极品videosbest最新版本| 国产精品扒开腿做爽爽爽的视频| 一个人www欧美| 日韩在线视频中文字幕| 亚洲欧美一区二区三区四区| 成人网欧美在线视频| 欧美国产日韩一区| 精品久久久久人成| 夜夜嗨av一区二区三区免费区| 精品国产自在精品国产浪潮| 亚洲人成在线观看网站高清| 久久免费在线观看| 久久69精品久久久久久久电影好| 最新中文字幕亚洲| 亚洲美女av在线播放| 另类天堂视频在线观看| 久久天天躁夜夜躁狠狠躁2022| 亚洲欧美资源在线| 亚洲性视频网站| 亚洲国产黄色片| 精品丝袜一区二区三区| 欧美激情一区二区三级高清视频| 午夜精品一区二区三区av| 国产精品91一区| 亚洲少妇中文在线| 狠狠做深爱婷婷久久综合一区| 欧美在线中文字幕| 亚洲国产精品久久久久| 欧美猛交免费看| 91精品在线国产| 亚洲热线99精品视频| 亚洲欧美日韩一区在线| 亚洲第一网站免费视频| 国产精品ⅴa在线观看h| 亚洲欧美第一页| 国产不卡精品视男人的天堂| 中文字幕日韩欧美精品在线观看| 亚洲日本aⅴ片在线观看香蕉| 欧美国产视频日韩| 日韩国产精品亚洲а∨天堂免| 成人情趣片在线观看免费| 欧美视频不卡中文| 欧美日韩成人黄色| 国产亚洲日本欧美韩国| 日日噜噜噜夜夜爽亚洲精品| 国产成人精品免高潮费视频| 亚洲精品第一国产综合精品| 亚洲激情在线观看| 夜夜躁日日躁狠狠久久88av| 丝袜美腿亚洲一区二区| 国产成人aa精品一区在线播放| 久久久999精品视频| 亚洲成人久久久| 欧美中文在线观看| 少妇精69xxtheporn| 一区二区亚洲欧洲国产日韩| 中文字幕精品av| 欧美在线观看视频| 韩国美女主播一区| 中文字幕国产精品久久| 成人黄色激情网| 亚洲精品自在久久| 51久久精品夜色国产麻豆| 欧美在线欧美在线| 日韩精品福利在线| 欧美日韩亚洲91| 久久精品久久久久电影| 91精品国产九九九久久久亚洲| 最新国产精品亚洲| 国产mv免费观看入口亚洲| 国产免费一区二区三区在线观看| 欧美日韩中文字幕日韩欧美| 亚洲精品国产精品自产a区红杏吧| 欧美激情一级二级| 中文字幕在线精品| 欧美成人国产va精品日本一级| 亚洲第一视频在线观看| 91美女片黄在线观看游戏| 国产精品久久久久久久久借妻| 日本欧美在线视频| 日韩电影视频免费| 国产精品亚发布| 57pao成人永久免费视频| 国产女人18毛片水18精品| 伊人伊成久久人综合网站| 精品亚洲一区二区三区在线播放| 中文字幕av一区中文字幕天堂| 欧美一区二区三区免费观看| 国产盗摄xxxx视频xxx69| 国产精品久久久久久久久粉嫩av| 尤物yw午夜国产精品视频| 亚洲视频综合网| 一区二区三区精品99久久| 国产美女91呻吟求| 九九热这里只有精品免费看| 91社区国产高清| 国产69精品久久久久9999| 久久精品青青大伊人av| 精品亚洲一区二区三区四区五区| 亚洲第一色中文字幕| 久久久中精品2020中文| 欧美区二区三区| 国产欧美在线视频| 成人444kkkk在线观看| 欧美成人精品不卡视频在线观看| 亚洲精品欧美日韩专区| 国产精品久久久久久久美男| 国内伊人久久久久久网站视频| 国内精品久久久久影院 日本资源| 久久综合免费视频| 亚洲女人天堂网| 97国产精品久久| www.日本久久久久com.| 久久91精品国产| 亚洲片国产一区一级在线观看| 久久久精品免费| 一级做a爰片久久毛片美女图片| 成人激情视频网| 成人免费自拍视频| 国产综合香蕉五月婷在线| 久久6精品影院| 色中色综合影院手机版在线观看| 国产精品美女免费| 国产精品丝袜视频| 国产亚洲欧洲高清| 亚洲综合色激情五月| 另类美女黄大片| 国产欧美一区二区白浆黑人|