zoukankan      html  css  js  c++  java
  • Ajax框架原理分析之Ext.Net

      Ext.Net也是一个很有名的前端框架,它构建于ExtJS之上,提供了一整套UI+AJAX的解决方案.通过对其源码的研究,了解其是如何实现这套AJAX的,对我们也是很有帮助的.

      1.静态的AJAX方法实现.

      当把[DirectMethod]标记标在一个静态方法上时,Ext.Net是通过HttpModule来截获Http请求实现的.具体的实现类为:DirectRequestModule类

      在应用程序生命周期的PostAcquireRequestState事件内中加入处理函数


    public void Init(HttpApplication app)
    {
        app.PostAcquireRequestState 
    += OnPostAcquireRequestState;
        app.PreSendRequestHeaders 
    += RedirectPreSendRequestHeaders;
    }


      如果是AJAX请求且是静态AJAX方法则调用ProcessRequest方法

    代码
    private void OnPostAcquireRequestState(object sender, EventArgs eventArgs)
    {
        HttpApplication app 
    = (HttpApplication)sender;
        HttpRequest request 
    = app.Context.Request;

        
    if (RequestManager.IsAjaxRequest)
        {
            
    if (DirectMethod.IsStaticMethodRequest(request) /*|| Utilities.ReflectionUtils.IsTypeOf(app.Context.Handler, "System.Web.Script.Services.ScriptHandlerFactory+HandlerWrapper")*/)
            {
                
    this.ProcessRequest(app, request);
            }
        }
    }


       通过反射来调用方法,并结束服务器处理过程,将结果返回到客户端(有删节)

    代码
    // Get handler
    HandlerMethods handler = HandlerMethods.GetHandlerMethods(context, request.FilePath);


    // Get method name to invoke
    string methodName = HandlerMethods.GetMethodName(context);

    DirectMethod directMethod 
    = handler.GetStaticMethod(methodName);

    object result = directMethod.Invoke();

    app.Context.Response.Clear();
    app.Context.Response.ClearContent();
    app.Context.Response.ClearHeaders();
    app.Context.Response.StatusCode 
    = 200;
    app.Context.Response.ContentType 
    = "application/json";
    app.Context.Response.Charset 
    = "utf-8";
    app.Context.Response.Cache.SetNoServerCaching();
    app.Context.Response.Cache.SetMaxAge(TimeSpan.Zero);
    app.Context.Response.Write(responseObject.ToString());
    app.CompleteRequest();


      因为是直接通过反射来实现,没有执行页面的生命周期,所以Ext.Net官方推荐此种编写方式.

      2.实例的AJAX方法实现.

      我们要重点关注ResourceManager类,它是整个AJAX请求的核心

      首先我们来看是如何实现AJAX方法的调动.

      当客户端发起一个AJAX请求时,Ext.Net会在POST数据中加入以下两个键值对:

      __EVENTARGUMENT:btnOK|event|Click

      __EVENTVALIDATION:ResourceManager1

      表示由ResourceManager类型的ResourceManager1实例来处理这次AJAX请求.具体发起请求的按钮是btnOK,形式为事件,方式为Click.

      在ResourceManager类的OnLoad事件中,如果是AJAX请求,则在LoadComplete事件中加入Page_AjaxLoadComplete处理函数

    代码
    protected override void OnLoad(EventArgs e)
    {
        
    base.OnLoad(e);

        
    if (RequestManager.IsAjaxRequest && !this.Page.IsPostBack && !this.IsDynamic)
        {
            
    this.Page.LoadComplete += Page_AjaxLoadComplete;
        }
    }


      在Page_AjaxLoadComplete处理函数中,借用回发的处理方法来实现AJAX调用:

    代码
    string _ea = this.Page.Request["__EVENTARGUMENT"];

    if (_ea.IsNotEmpty())
    {
        
    string _et = this.Page.Request["__EVENTTARGET"];

        
    if (_et == this.UniqueID)
        {
            
    this.RaisePostBackEvent(_ea);
        }

        
    return;
    }


      在RaisePostBackEvent方法中,实现对具体控件具体方法的具体调用:

    代码
    string controlID = args[0];
    string controlEvent = args[2];

    ctrl 
    = ControlUtils.FindControlByClientID(this.Page, controlID, truenull);

    case AjaxRequestType.Event:
        Observable observable 
    = ctrl as Observable;

        
    if (observable == null)
        {
            
    if (ctrl is ResourceManagerProxy)
            {
                ((ResourceManagerProxy)ctrl).FireAsyncEvent(controlEvent, extraParams);
            }
            
    else if (ctrl is ResourceManager)
            {
                
    this.FireAsyncEvent(controlEvent, extraParams);
            }
            
    else
            {
                
    throw new HttpException("The control with ID '{0}' is not Observable".FormatWith(controlID));
            }
        }
        
        
    if (observable != null)
        {
            observable.FireAsyncEvent(controlEvent, extraParams);
        }
        
    break;


      然后我们来看Ext.Net是如何将处理结果返回给客户端的.

      (29日继续)

      首先我们要明白一点,后台的Ext.Net控件在输出到客户端的并不是HTML代码,而是JSON包装后的JS代码。浏览器执行接收到的JS代码后再生成HTML代码。

      我们知道,控件的输出一般都写在生命周期的Render事件中。XControl是所有Ext.Net控件的基类。这个类比较大,作者用了近十个文件,采用分部类的开发方式来实现这个类。Lifecycle.cs文件主要负责重写控件的生命周期。在其重写的Render中,调用了HtmlRender方法。此方法是一般的具体控件的实际输出方法。另外在ResourceManager类的RenderAction方法中输出页面脚本注册,样式注册,页面初始化脚本等等。这些输出不是单纯的输出HTML或JS代码,而是在输出的内容的两端加上了类似于“<Ext.Net.Direct.Response>”的标签,这是为下一步输出过滤做准备。

      接着就要看输出过滤了。我们知道,传统的Asp.Net提交,服务器会完成整个页面的生命周期,之后将处理过后的整个页面的内容返回。但是Ext.Net的AJAX提交走完了整个页面的生命周期,返回的却是Json数据。这其中倒底有什么玄机?答案就在输出过滤!(光寻找这一点我就花了近三个小时,原因是我在听WebCast的的时候,老赵说Asp.net Ajax框架是在Render事件上做手脚,对内容进行了重输出,我想Ext.Net应该差不多,于是使劲找,结果什么也找不到。  —_—!)

      

      还是在DirectRequestModule类中:

    public void Init(HttpApplication app)
    {
        app.ReleaseRequestState += AjaxRequestFilter;
    }

      一开始我以为没什么用,现在才知道这是处理AJAX返回数据的关键!

    代码
    HttpResponse response = HttpContext.Current.Response;

    if (RequestManager.IsAjaxRequest)
    {
        
    if (response.ContentType.IsNotEmpty() && response.ContentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))
        {
            response.Filter = new AjaxRequestFilter(response.Filter);
        }
    }


      在这里,AjaxRequestFilter类是主要实现类。其Flush方法是关键方法。 里面用到的DirectResponse类是返回的包装类,将可能的返回信息封装成了一个类,我们一步一步的看。

    string raw = this.html.ToString();

    StringBuilder buffer 
    = new StringBuilder(256);

    DirectResponse ajaxResponse 
    = new DirectResponse(true);
    HttpContext context 
    = HttpContext.Current;

    这里是一些初始化的工作。其中html就是待过滤的原始的html代码。

    object isUpdate = context.Items["Ext.Net.Direct.Update"];

    if (isUpdate != null && (bool)isUpdate)
    {
        
    this.ExtractUpdates(raw, ref buffer);
    }

    这里是从原始html提取更新的部份并写到buffer中去。思路是用正则去匹配的,具体实现自行看代码!

    代码
    string dynamicHtml = this.ExtractDynamicHtml(raw);

    object isManual = context.Items["Ext.Net.Direct.Response.Manual"];

    if (isManual != null && (bool)isManual)
    {
        
    if (raw.StartsWith("<Ext.Net.Direct.Response.Manual>"))
        {
            
    string script = dynamicHtml.ConcatWith(raw.RightOf("<Ext.Net.Direct.Response.Manual>").LeftOf("</Ext.Net.Direct.Response.Manual>"));
            
    byte[] rsp = System.Text.Encoding.UTF8.GetBytes(script);
            
    this.response.Write(rsp, 0, rsp.Length);
            
    this.response.Flush();
            
    return;
        }
    }

    buffer.Append(dynamicHtml);

    这里是从原始的html提取动态生成的html代码,这里有个小插曲,如果isManual与raw满意要求的话,就直接将提取的结果返回了。否则也将结果写到buffer中。

     

    代码
    string error = context == null ? null : (context.Error != null ? context.Error.ToString() : null);

    if (!ResourceManager.AjaxSuccess || error.IsNotEmpty())
    {
        ajaxResponse.Success 
    = false;

        
    if (error.IsNotEmpty())
        {
            ajaxResponse.ErrorMessage 
    = error; 
        }
        
    else
        {
            ajaxResponse.ErrorMessage 
    = ResourceManager.AjaxErrorMessage; 
        }
    }

    这里是错误处理,就不多说了。

    代码
    if (ResourceManager.ReturnViewState)
                    {
                        ajaxResponse.ViewState 
    = AjaxRequestFilter.GetHiddenInputValue(raw, BaseFilter.VIEWSTATE);
                        ajaxResponse.ViewStateEncrypted 
    = AjaxRequestFilter.GetHiddenInputValue(raw, BaseFilter.VIEWSTATEENCRYPTED);
                        ajaxResponse.EventValidation 
    = AjaxRequestFilter.GetHiddenInputValue(raw, BaseFilter.EVENTVALIDATION);
                    }

    这里是从原始的html提取ViewState

    代码
    object obj = ResourceManager.ServiceResponse;

    if (obj is Response)
    {
        ajaxResponse.ServiceResponse 
    = new ClientConfig().Serialize(obj);
    }
    else
    {
        ajaxResponse.ServiceResponse 
    = obj != null ? JSON.Serialize(obj) : null;
    }

    if (ResourceManager.ExtraParamsResponse.Count > 0)
    {
        ajaxResponse.ExtraParamsResponse 
    = ResourceManager.ExtraParamsResponse.ToJson();
    }

    if (ResourceManager.DirectMethodResult != null)
    {
        ajaxResponse.Result 
    = ResourceManager.DirectMethodResult;
    }

    这里是从ResourceManager类中获取指定数据。

    buffer.Append(raw.RightOf("<Ext.Net.Direct.Response>").LeftOf("</Ext.Net.Direct.Response>"));

    if (buffer.Length > 0)
    {
        ajaxResponse.Script 
    = "<string>".ConcatWith(buffer.ToString());
    }

    这里是从原始的html提取被"<Ext.Net.Direct.Response>"标记的内容。写到buffer中,并终赋到ajaxResponse.Script属性中。

     

    代码
    byte[] data = System.Text.Encoding.UTF8.GetBytes((isUpload ? "<textarea>" : ""+ ajaxResponse.ToString() + (isUpload ? "</textarea>" : "") );
    this.response.Write(data, 0, data.Length);

    this.response.Flush();

    最后,将结果通过DirectResponse类重写过的ToString方法将结果序列化输出到客户端。


      总结输出的过程,其实是从原始的HTML代码提取出相关信息填充到DirectResponse类的相关属性中,再将其序列化到客户端的过程。从ResourceManager获取ServiceResponse,ExtraParamsResponse,Result,从原始HTML获取ViewState,ViewStateEncrypted,EventValidation,从原始HTML获取更新的数据,动态的HTML内容,"<Ext.Net.Direct.Response>"并填入Script属性,跟据需要设置Success与ErrorMessage属性。通过重写DirectResponse类的ToString方法,来实现序列化过程。

      以上就是我的分析。可能非常的粗枝大叶,但整个流程的基本架构是分析出来了。 虽然代码具体实现的好坏可能仁者见仁,智者见智,但里面真的还是有很多东西值得我去学习的!

  • 相关阅读:
    [转]MySQL索引类型
    [转]数据结构(全)
    [转]数据结构基础概念篇
    [转]从零开始搭建创业公司后台技术栈
    将博客搬至CSDN
    编译android源代码(aosp)
    【Android】Fresco图片加载框架(二)————Producer
    【Android】Fresco图片加载框架(一)————源码简要分析
    TSL(SSL)相关内容
    谨慎使用AsyncTask
  • 原文地址:https://www.cnblogs.com/ljzforever/p/1888502.html
Copyright © 2011-2022 走看看