zoukankan      html  css  js  c++  java
  • 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 的超轻异步框架,千行代码完成。

    源码下载

    点击这里下载

  • 相关阅读:
    jquery.cookie.js 的使用
    2013年工作中遇到的20个问题:141-160
    提高生产力:文件和IO操作(ApacheCommonsIO-汉化分享)
    提高生产力:文件和IO操作(ApacheCommonsIO-汉化分享)
    我的网站恢复访问了,http://FansUnion.cn
    我的网站恢复访问了,http://FansUnion.cn
    噩梦遇地震,醒后忆岁月
    噩梦遇地震,醒后忆岁月
    2013年工作中遇到的20个问题:121-140
    2013年工作中遇到的20个问题:121-140
  • 原文地址:https://www.cnblogs.com/scy251147/p/3082151.html
Copyright © 2011-2022 走看看