老生常谈——JS调用C#的callback实现

背景:
   2007年伊始,陈同学临时接收公司BS系统的异步提交升级的任务,不久之前微软已经推出了ASP.NET AJAX的第一套标准框架以及控件这其中就包括updatepanel,没错,正是那个只要把传统web控件往里一扔便能使其自动异步提交的“万金油”,陈同学一时之间以为找到了BS系统异步提交的完全解决方案,但是当他摸索着这条看似简易可行的线索继续向前时却发现一个很严重的问题,那就是updatepanel本身的机制存在缺陷,毕竟要设计为可包装web控件就直接异步提交或触发服务器事件的设计必定在其本身的实现上有太多太多的脚本需要首先供客户端解析,也就是说updatepanel就是个包裹,其设计思路走的是包装器模式路线,所以对于每个要异步的控件都要特别的做出额外的功能代码包装,以下引用陈同学的原话:
   “UpdatePanel在页面小的时候还是很好用的,而当页面控件数不断上升的时候,UpdatePanel就开始直线下降,我们现在页面有4,5百个控件,每做一次PostBack需要长达15秒钟之长,实在让人无法忍受。”
    本文不想对updatepanel的机制进行深入的探讨,有兴趣可以自己上网找资料
    终于在一方面不能忍受updatepanel的慢动作一方面又难以承受来自公司的BS异步升级的压力下(主要是来自公司的压力),陈同学另辟蹊径终于在一片黑暗当中找到了一盏摇曳的风中之烛,那就是传说中的callbackplus(通过对smartscript以及callbackplus的研习笔者发现陈同学肯定是一个严重走火入魔的标题党),
通过完成callbackplus陈同学使得公司以后的万千产品都能够平滑地实现数据异步提交以及脚本和C#之间的相互通讯,这简直是造福了一方代码民众的壮举啊。
    事隔N年(N == 3)后初到公司的笔者便在其导师的指导下认识并使用了陈同学当年的这一惊世骇俗的作品
      “这就是我们公司唯一可以拿出来当事说的东西了” —— by 导师周某
     在亲身体验到前辈所创造出的惊世作品之后,在某个不是很忙的季节,笔者终于忍不住想一窥究竟,但苦于请求源码开放的要求实在难以启齿,所以只好自己一点一点地琢磨开了。

 

情节:

以下就对callbackplus的实现机制和原理进行阐述,看完以后应该也能做一个简单的callbackplus吧、首先还是要提醒大家在阅读之前要注意的几点
1  在ASP.NET AJAX的正式框架提出之前实际上微软就已经有对于callback的支持了其实现的方法这里不赘,请参考MSDN
   读者首先需要认识

                   1  ClientScript.GetCallbackEventReference();是得到支持callback的控件的在页面中生成的JS代码的方法
                   2  ICallbackEventHandler 接口以及其契约方法RaiseCallbackEvent(string argument)以及GetCallbackResult(),自己去查MSDN,这里一句话就是:前面的那个方法是用来处理从页面传来的参数的,后面的那个方法是用来return在服务器端处理以后的数据到客户端脚本的

当读者能够读完并理解以上的内容以后,下面我们进入正题(激动ing)——callbackplus的实现原理

1  当前需要回调的用户自定义控件或page或所属的控件类必须是实现了ICallbackEventHandler 接口的,也就是重写了RaiseCallbackEvent和GetCallbackResult两个方法

2  通过Reflector机制反射遍历所有当前页面或者当前控件当中含有[callbackmethod]特性的方法
3  通过步骤2可以获得当前需要回调(callback)的方法名称,然后在服务端拼接JS代码,比如说当前的方法名叫helloworld那么拼接以后的就是function helloworld(),

在ClientScript.GetCallbackEventReference();方法执行以后页面还应该包含有调用以方法生成的JS代码,名称为:
WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync);
这样就行了,这里想啰嗦两句关于WebForm_DoCallback这个由微软提供的JS方法的作用:

 它执行了对当前浏览器对异步的支持的判断(要么是XMLHTTPRequest大对象要么就是传统的IFrame),以及通过参数表我们可以看到它能够对传入当前这个JS方法的参数作保留并且一并传输到服务器,有同学问传到服务器又是谁来接收呢?看看步骤一中的RaiseCallbackEvent吧,有同学又问传到服务器以后,服务器怎么知道执行哪一个C#方法呢,那么再看看步骤一中的RaiseCallbackEvent吧(没写错还是一样的方法),在这里我们可以得到JS方法名称以及想要调用到的服务器方法名称,想想吧WebForm_DoCallback方法的第二个参数,说穿了就是想传啥就传啥,再说穿一点就是拼字符串啦,说得太穿笔者觉得很没劲啊。又有同学问了,那要是我在执行完服务器端代码以后还要指定一个客户端的回调函数呢?那你就再看看这个方法的第三个参数,但是还有同学问要是我C#方法要返回一个值,JS又是怎么得到的呢,那就再看看步骤一下的GetCallbackResult()方法,它将返回C#代码处理过的结果到客户端,这里把反编译出来的GetCallbackResult并render到前台JS代码中的代码附在下面:
private void RenderCallback()
{
      bool flag1 = !string.IsNullOrEmpty(this._requestValueCollection["__CALLBACKLOADSCRIPT"]);
      try
      {
            string text1 = null;
            if (flag1)
            {
                  text1 = this._requestValueCollection["__CALLBACKINDEX"];
                  if (string.IsNullOrEmpty(text1))
                  {
                        throw new HttpException(SR.GetString("Page_CallBackInvalid"));
                  }
                  for (int num1 = 0; num1 < text1.Length; num1++)
                  {
                        if (!char.IsDigit(text1, num1))
                        {
                              throw new HttpException(SR.GetString("Page_CallBackInvalid"));
                        }
                  }
                  this.Response.Write("<script>parent.__pendingCallbacks[");
                  this.Response.Write(text1);
                  this.Response.Write("].xmlRequest.responseText=\"");
            }
            if (this._callbackControl != null)
            {
                  string text2 = this._callbackControl.GetCallbackResult();
                  if (this.EnableEventValidation)
                  {
                        string text3 = this.ClientScript.GetEventValidationFieldValue();
                        this.Response.Write(text3.Length.ToString(CultureInfo.InvariantCulture));
                        this.Response.Write('|');
                        this.Response.Write(text3);
                  }
                  else
                  {
                        this.Response.Write('s');
                  }
                  this.Response.Write(flag1 ? Util.QuoteJScriptString(text2) : text2);
            }
            if (flag1)
            {
                  this.Response.Write("\";parent.__pendingCallbacks[");
                  this.Response.Write(text1);
                  this.Response.Write("].xmlRequest.readyState=4;parent.WebForm_CallbackComplete();</script>");
            }
      }
      catch (Exception exception1)
      {
            this.Response.Clear();
            this.Response.Write('e');
            if (this.Context.IsCustomErrorEnabled)
            {
                  this.Response.Write(SR.GetString("Page_CallBackError"));
                  return;
            }
            this.Response.Write(flag1 ? Util.QuoteJScriptString(HttpUtility.HtmlEncode(exception1.Message)) : HttpUtility.HtmlEncode(exception1.Message));
      }
}

     实际上要得到服务器端返回的计算结果没有想象的那么难啦,上面的代码供娱乐用,下面贴出典型的得到返回值的方法,首先前提是要指定回调函数比如说叫
    BabySoHot,那么你的回调函数代码应该是这样的
    function BabySoHot(result, context) 
    { 
       //TODO  
    } 
    看到第一个形参了吗?对,result,这个就是你想获取的东西啦(这种感觉好WCF啊)。。。。。。。。。。。。。。。。。。。。。。  感觉好先进好先进哦

当然这当中还牵扯到很多关于JS以及C#的数据类型不兼容的情况,而这具体的实现就要具体讨论了笔者的意见是不妨试试json,
最后补充一下把C#拼接出来的JS代码注册到页面当中的方法就不多说了吧,比如说RegisterClientScriptBlock等等还有很多,要看你想把当前的JS注册到页面的什么位置来定咯。

转载于:https://www.cnblogs.com/zhengyin/archive/2010/03/31/1701815.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值