Asp.net超輕異步框架


asp.net異步的不當使用方式

在進行asp.net ajax設計的時候,我們肯定避免不了利用JQuery的ajax函數取調用HttpHandler中的數據.在我開始學習的時候,我總是這么用的,那時候頭腦中沒有什么概念,只知道有了新需求就新增ashx文件,復制粘貼原有的ajax請求代碼,稍微修改一下即可. 所以文件中總是充斥着大量的可粘貼復制的代碼:

 $.ajax({ type    : "post",
    contentType: "application/json",
    datatype   : "json",
    url        : "WebHandler.ashx/GetUserList",
    data       : "{UID:1}",
    success    : function(data) {
      alert("ok")
    }
});

這樣做的好處就是自己很開心,也費不了多少時間就可以將功能修改好.但是帶來的負面效果將是致命的.

(1) 大量的代碼結構基本上一致,粘貼復制不但容易出錯,而且項目一大,修改起來就比較麻煩.倘若一個地方出錯, 好多地方修改.

(2) 代碼中的URL請求非常不保險,一旦項目結構發生變化,路徑發生變化,項目的修改就需要跟進,項目一大,這種修改是致命的.

(3) 這種工作不能很好的協調前端開發和后端開發, 由於前端直接通過ajax請求后端數據,導致二者的配合需要非常緊密(前端設計有時候需要看后端代碼去確定需要調用哪個方法).不好獨立的進行開發.

(4) 就是有違軟件設計軟則,大量重復代碼充斥,后期維護困難,牽一發而動全身.

比較好的解決方式

所以基於以上幾點,決定針對這四點着重解決.

(1) 大量重復代碼可以寫到一個公共的js文件中,然后將請求文件參數當作參數傳出:

$.ajaxRequest("WebHandler.ashx/GetUserList ", "{}", function(result) {......});

這樣使用的時候,直接添加好js文件,然后調用即可.只關注邏輯,代碼量顯著下降.

(2) 當項目文件改變的時候,上面的方法顯然無法解決問題,所以最好能夠將url自動檢測. 所以我期望的是這樣調用:

$.ajaxRequest.GetUserList (“{}”, function (result {……}) ;

這樣就解決了項目變動,不能找到handler文件的問題.

(3) 這個問題,可以通過新增一個中間文件來解決.最好的辦法就是客戶端請求時,后台能夠動態生成供前台調用的js代碼.

(4) 這個主要是代碼編寫這塊. 與設計方面暫無關系.

所以,總結一下,就是由原先的前台通過ajax直接發送請求到后台,然后后台返回數據的過程, 修改成了前台通過ajax發出請求,后台動態生成js中間文件,前台然后調用即可.

 

所以這里我總結一下我們的需求,

就是,前台發送$.ajaxRequest.GetUserList (“{}”, function (result {……}) ;出去,后台接收之后,動態創建js文件,然后供前台調用.

那么這里我們需要的東西就是,一個js模板,能夠承載動態生成的函數, 一個ashx文件,能夠接收請求,並且讀取模板並修改之.

代碼閱讀

模板代碼我就不寫了,直接找現成的, 感謝作者的開源:

/*----------------------------------------------------------------------------
--功能說明:   %H_DESC%
--創建時間:   %H_DATE%
--其它信息:   此文件自動生成,並依賴json2.js <http://www.JSON.org/json2.js>
--內核維護:   wzcheng@iflytek.com 
------------------------------------------------------------------------------*/

(function($) {

    if (!$.net) {

        var defaultOptions = { contentType: "application/json; charset=utf-8"
                , dataType: "json"
                , type: "POST"
              //, complete: function(r, status) { debugger; } //此代碼加上用於全局調試
        };

        //將net作為命名空間擴展到jQuery框架內
        $.extend({ net: {} });

        //將調用WEB SERVICES的代理函數CallWebMethod擴展到jQuery.svr框架內
        $.extend($.net, {

            CallWebMethod: function(options, method, args, callback) {

                //調用第三方對象序列化成JSON字符串的方法
                var jsonStr = JSON.stringify(args);

                var parameters = $.extend({}, defaultOptions);

                var url0 = options.url + "/" + method;

                $.extend(parameters, options, { url: url0, data: jsonStr, success: callback });

                $.ajax(parameters);
            }
        });
    }

    //將指定類型的WEB服務擴展到jQuery框架內
    var services = new %CLS%();
    $.extend($.net, { %CLS%: services });

})(jQuery);

/*----------------------------------------------------------------------------
--功能說明: 服務的構造函數
----------------------------------------------------------------------------*/
function %CLS%() {
    /*
    --定義本地的調用選項,如果希望改變個別的ajax調用選項,
    --請在對象中添加其它選項的鍵/值
    */
    this.Options = { url: "%URL%" };
}
 
//以下為系統公開的可調用方法
View Code

然后就是后台代碼,我已經加入了主要的注釋,各位看官請看好.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.SessionState;
using System.Reflection;
using System.IO;
using System.Web.Script.Serialization;
using System.Collections.Specialized;
using System.Web.Script.Services;


namespace EDaemonCore
{
    public class CoreHandler : IHttpHandler, IRequiresSessionState
    {

        private HttpContext context;

        public bool IsReusable
        {
            get { return true; }
        }

        /// <summary>
        /// Request請求入口
        /// </summary>
        public void ProcessRequest(HttpContext context)
        {
            this.context = context;

            //獲取函數簽名並將其觸發
            string inputMethod = GetInputMethod();
            object result = Invoke(inputMethod,GetParameterValue());

            //生成JS模板
            StringBuilder sbStr = GenerateJsTemplate();

            //將結果打印出去
            context.Response.Write(result);
        }

        /// <summary>
        /// 得到JS模板,並將其中的關鍵字做替換,能夠解決目錄或者是名稱變更,找不到handler文件的問題.
        /// </summary>
        public StringBuilder GenerateJsTemplate()
        {
            Type type = this.GetType();

            Uri url = HttpContext.Current.Request.Url;
            string script = GetJsTemplate();
            script = script.Replace("%H_DESC%", "通過jQuery.ajax完成服務端函數調用");
            script = script.Replace("%H_DATE%", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            script = script.Replace("%URL%", url.Query.Length > 0 ? url.ToString().Replace(url.Query, "") : url.ToString());
            script = script.Replace("%CLS%", type.Name);

            StringBuilder scriptBuilder = new StringBuilder(script);

            MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

            foreach (MethodInfo m in methods)
            {
                //ResponseAnnotationAttribute resAnn = this.GetAnnation(m.Name);

                //scriptBuilder.AppendLine("/*----------------------------------------------------------------------------");
                //scriptBuilder.AppendLine("功能說明:" + resAnn.Desc);
                //scriptBuilder.AppendLine("附加說明:緩存時間 " + resAnn.CacheDuration.ToString() + " 秒");
                //scriptBuilder.AppendLine("         輸出類型 " + resAnn.ResponseFormat.ToString());
                //scriptBuilder.AppendLine("----------------------------------------------------------------------------*/");

                string func = GetFunctionTemplete(m);
                scriptBuilder.AppendLine(func);
            }
            return scriptBuilder;
        }

        /// <summary>
        /// 將后台業務代碼動態添加到JS文件中,供前台調用
        /// </summary>
        private static string GetFunctionTemplete(MethodInfo method)
        {
            StringBuilder func = new StringBuilder(method.DeclaringType.Name);
            func.Append(".prototype." + method.Name);
            func.Append("=function");

            func.Append("(");
            foreach (ParameterInfo p in method.GetParameters())
            {
                func.Append(p.Name + ",");
            }
            func.Append("callback)");

            func.AppendLine("{");
            {
                func.Append("\tvar args = {");
                foreach (ParameterInfo p in method.GetParameters())
                {
                    func.Append(p.Name + ":" + p.Name + ",");
                }
                func.AppendLine("ajax:'jquery1.4.2'};");
                //switch (format)
                //{
                //    case ResponseFormat.Xml:
                //        func.AppendLine("\tvar options={dataType:'xml'};");
                //        break;
                //    case ResponseFormat.Json:
                //        func.AppendLine("\tvar options={dataType:'json'};");
                //        break;
                //    default:
                //        func.AppendLine("\tvar options={dataType:'text'};");
                //        break;
                //}
                func.AppendLine("\tvar options={dataType:'text'};");
                func.AppendLine("\t$.extend(true,options,{},this.Options);");
                func.AppendFormat("\t$.net.CallWebMethod(options,'{0}', args, callback);", method.Name);
                func.AppendLine();
            }
            func.AppendLine("}\t\t");

            return func.ToString();
        }

        /// <summary>
        /// 文件流操作,讀取JS模板文件
        /// </summary>
        private string GetJsTemplate()
        {
            Type type = typeof(CoreHandler);

            AssemblyName asmName = new AssemblyName(type.Assembly.FullName);

            Stream stream = type.Assembly.GetManifestResourceStream(asmName.Name + ".ScriptDaemon.net.js");

            if (stream != null)
            {
                byte[] buffer = new byte[stream.Length];
                int len = stream.Read(buffer, 0, (int)stream.Length);

                string temp = Encoding.UTF8.GetString(buffer, 0, len);

                return temp;
            }
            else
            {
                throw new Exception("模版未找到");
            }
        }

        /// <summary>
        /// 獲取當前請求的信息,如果有請求函數,則轉至請求函數,如果沒有,則代表需要生成動態JS文件
        /// </summary>
        private string GetInputMethod()
        {
            string[] segmentCollection = this.context.Request.Url.Segments;
            int segmentLength = segmentCollection.Length;
            string inputMethod = segmentCollection[segmentLength - 1];

            if (inputMethod.LastIndexOf(".ashx") >= 0)
                inputMethod = "GenerateJsTemplate";

            return inputMethod;
        }

        /// <summary>
        /// 動態調用有參/無參methodName方法並返回結果
        /// </summary>
        /// <param name="methodName">函數簽名</param>
        /// <param name="args">參數內容</param>
        /// <returns>返回內容</returns>
        private object Invoke(string methodName, Dictionary<string, object> args)
        {
            MethodInfo specificMethod = this.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public);
            if (specificMethod == null)
                throw new Exception("The method is not exist, pls check.");

            List<object> argsList = new List<object>();

            ParameterInfo[] parameterInfo = specificMethod.GetParameters();  // Get the parameters

            foreach (ParameterInfo p in parameterInfo)  //Loop
            {
                if (args.ContainsKey(p.Name))
                {
                    object obj = args[p.Name]; // get parameter value
                    argsList.Add(Convert.ChangeType(obj, p.ParameterType));
                }
            }

            object[] parameters = argsList.ToArray();
            object result = specificMethod.Invoke(this, parameters);
            return result;
        }

        /// <summary>
        /// 動態獲取參數並保存
        /// </summary>
        private Dictionary<string, object> GetParameterValue()
        {
            Stream inputStream = this.context.Request.InputStream;
            inputStream.Position = 0;  //reset the position to 0

            byte[] buffer = new byte[inputStream.Length];
            inputStream.Read(buffer, 0, buffer.Length); //read stream data into buffer

            Encoding inputEncoding = this.context.Request.ContentEncoding;
            string inputStr = inputEncoding.GetString(buffer);

            JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
            object obj = jsSerializer.DeserializeObject(inputStr);

            Dictionary<string,object> paramDict = obj as Dictionary<string, object>;
            NameValueCollection queryStr = this.context.Request.QueryString;
            foreach (string name in queryStr)
            {
                paramDict.Add(name,queryStr[name]);
            }
            return paramDict;
        }
    }
}
View Code

總之,操作過程就是,有請求發來,就動態在JS中生成與服務端一致的簽名函數,然后客戶端調用.

這里,我們可以添加點函數來測試:

后台:

 public string GetTestMessage(int flag,string content)
        {
            return string.Format("User {0} says: this is {1} message.", flag, content);
        }

        public string GetMessageTest()
        {
            return string.Format("content:haha this is a ttttest.");
        }
View Code

前台調用部分:

 <script src="WebHandler.ashx" type="text/javascript"></script>
        
        <script type="text/javascript">
        $(document).ready(function(){
        
            $("#Button1").bind("click",function(){
                $.net.WebHandler.GetMessageTest(function(data){
                    alert(data);
                });
            });
            
             $("#Button2").bind("click",function(){
                $.net.WebHandler.GetTestMessage(731,'ShiChaoYang',function(data){
                    alert(data);
                });
            });
        });
        </script>
View Code

看上去是不是簡潔了許多?

需要說明的是,我們還有很多方式來讓前台調用后台,除了這種利用反射來動態生成中間JS文件以外,我們還可以通過在服務端維護一個Dictionary來進行,Dictionay的key存儲函數簽名,value存儲函數體,這也是一種不錯的設計方式.

效果圖展示

參考:

非常感謝這篇博客,一個基於jQuery ajax和.net httphandler 的超輕異步框架,千行代碼完成。

源碼下載

點擊這里下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM