Writing Lightweight AJAX Components 01-Compare the various implementations on the WebForm platform

Source: Internet
Author: User

Objective

ASP. NET WebForm and ASP. NET MVC (MVC) are based on the Web development framework of ASP, which is very different, one of which is that MVC pays more attention to HTTP nature, and WebForm tries to block HTTP, which provides a lot of server controls and viewstate mechanisms, so developers can develop windows Like the form application, it is programmed based on the event model. Both have pros and cons, but MVC is now the first choice for many ASP.

WebForm is built on ASP. ASP. NET provides enough extensibility, and we can also use these to write MVC-like frameworks under WebForm, and this has the opportunity to write again. When it comes to WebForm, many people think of server controls (drag Controls!!!) ), in fact, we can also completely not use server controls, like MVC as the focus on HTML. WebForm to abandon the server control, focus on HTML, the first thing is to <form runat= "server" ></form> tag removed, this runat server form is the basis of its postback mechanism. Now that we're going back to html+css+js, it means that a lot of things have to be implemented on their own, such as handling AJAX requests. Unlike MVC, the design of WebForm begins with the server control as the main component, and if it is not used, it can only be implemented with its extensibility.

This series is to implement a lightweight Ajax component based on the WebForm platform, which is divided into three main parts:

  1. Introduce various realization methods under WebForm.

2. Analyze Ajaxpro components.

3. Write your own AJAX components.

First, Ajax introduction

Async allows us to request or submit data like a server without refreshing the entire page. For complex pages, overloading the entire page to request a bit of data is obviously inefficient, and Ajax is designed to solve this problem. At the core of Ajax is the XMLHttpRequest object, which submits the request to the server in text form. After XmlHttpRequest2.0, the submission of binary data is also supported.

Ajax security: For security reasons, Ajax is restricted by the same-origin policy, that is, requests that only access the same domain, the same port, and cross-domain requests are denied. Of course, sometimes requirements require cross-domain sending requests, and common cross-domain processing methods are cors (cross-domain resource sharing) and JSONP (parametric JSON).

Ajax Data Interchange Format: Although the Ajax core object XMLHttpRequest has "XML" word, but the client-server data exchange format is not limited to XML, for example, now more is used in JSON format.

Ajax has drawbacks, too. For example, support for search engines is not good, and sometimes it violates the purpose of URL resource targeting.

Ii. using AJAX under ASP.

In MVC, it is very convenient for Ajax to invoke the background method, just specify the name of the action.

Front Code:

<body>    

Background code:

     public class Ajaxcontroller:controller    {public        actionresult GetData ()        {            if ( Request.isajaxrequest ())            {                return Content ("data");            }            return View ();        }    }

Third, the use of Ajax WebForm platform

  3.1 Server-based control packages or third-party components

This is a server-based control, such as an AJAX Toolkit toolkit, or a component such as Fineui. The web front end is always composed of Html+css+js , just how to generate the problem. Native we can write it ourselves, or use some front-end plugins; server-based controls are generated in the background and are usually less efficient. The server component generates a series of proxies in the foreground, essentially the same, except that the control encapsulates the process and does not need to be written by us. Patterns based on controls or third-party components are useful in some management systems, and access is not very large and can be developed quickly.

  3.2 Based on ICallbackEventHandler interface

. NET provides a ICallbackEventHandler interface for handling callback requests. The interface needs to generate agent scripts in the foreground with ClientScriptManager for sending and receiving requests, so <form runat= "server" > tags are required.

Front Code:

<body>    <form id= "Form1" runat= "Server" >    <div>                <input type= "button" value= "Get callback result" 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>

Background code:

    Public partial class Test1:System.Web.UI.Page, ICallbackEventHandler    {                protected void Page_Load (object sender , EventArgs e)        {            //client Script Manager            ClientScriptManager scriptmgr = this. ClientScript;            Get the callback function, GetCallbackResult is the callback function            string functionname = Scriptmgr.getcallbackeventreference (This, "", "" GetCallbackResult "," ");            The script that initiates the request, CallServer is the execution function of the Click button Event            string scriptexecutor = "function CallServer () {" + functionname + ";}";            Register Script            Scriptmgr.registerclientscriptblock (this. GetType (), "CallServer", Scriptexecutor, True);        }        Interface method Public        string GetCallbackResult ()        {            return "callback result";        }        interface method public        void RaiseCallbackEvent (String eventargument)        {        }    }

This approach has the following drawbacks:

1. More complex to implement, each page load event to register the corresponding script.

2. The foreground generates a script file for the agent.

3. For a complex page interaction, it is very cumbersome to implement.

4. Although it is a callback, the page object is generated at this time.

  3.3 Using generic handlers

The general handler is actually a IHttpHandler interface class, which, like the page class, can also be used to process requests. Generic handlers are not typically used to generate HTML or complex event mechanisms, and only one ProcessRequest portal is used to process requests. We can write the AJAX request address as the path to the. ashx file so that it can be processed and more efficient.

To output text content requires only Response.Write (data), for example, after fetching data from a database, serializing it into a JSON-formatted string and outputting it. As mentioned earlier, the generic handler does not generate HTML as it was on the page, and if it is to generate HTML, it can be generated by loading the user control. Such as:

        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);                            }        }

The advantage of this approach is that it is lightweight and efficient, and the disadvantage is that many ashx files need to be defined for multiple interactions, which increases management and maintenance costs.

  3.4 Page Base class

The method that processes the AJAX request is defined within the Page object so that each page can focus on the requests related to this page. There's a little bit to note here.

1. How do I know if this request is an AJAX request?

By requesting x-requested-with:xmlhttlrequest, it can be judged that the request header is included in most of the browser's asynchronous requests, or through custom request headers, for example:ajaxflag:xhr.

2. Where is the uniform processing?

If it is cumbersome to judge and invoke in each of the page classes, the process is transferred to a page base class.

3. How do I know which method is being called?

You can either pass a parameter or define it in the request header, for example: Methodname:getdata.

4. Know the method name, how to invoke it dynamically?

    reflection .

5. How do I know if this method can be called externally?

It can be assumed that the public type can be called externally, or it can be tagged with a tag attribute.

Through the above analysis, simple implementation of the following

Page base class:

    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 tag cannot be empty!                "); } Type type = this. GetType ().                BaseType; MethodInfo info = type. GetMethod (MethodName, BindingFlags.Public | BindingFlags.Instance |                BindingFlags.Static); if (info = = null) {EndRequest ("the appropriate method call cannot be found!")                "); } 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 (); }    }

Page class:

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

Front Code:

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

Four, the optimized version of the page base class

The page base class above has few functions, and it is inefficient to call by reflection. Here to optimize:

1. Parameters with simple types can be supported.

For example, the above GetData can be: GetData (string name), through the function metadata can get the relevant parameters, and then according to the parameters of the request, you can set the parameters.

2. Add tag attributes.

Only attributes that are tagged by AjaxMethodAttribute can be called externally.

3. Optimize Reflections.

Use the cache to avoid searching for function information each time according to the function name.

Tag attributes:

    public class Ajaxmethodattribute:attribute    {    }

Cached objects:

    public class Cachemethodinfo    {public        string MethodName {get; set;}        Public MethodInfo MethodInfo {get; set;}        Public parameterinfo[] Parameters {get; set;}    }

Base class Code:

     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>//Reflection execution Function///</summary>//<param name= "MethodName" ></ param> private void InvokeMethod (string methodName) {if (string. IsNullOrEmpty (MethodName)) {EndRequest ("MethodName tag cannot be empty!            ");            } cachemethodinfo targetinfo = Trygetmethodinfo (methodName); if (Targetinfo = = null) {EndRequest ("the appropriate method call cannot be found!")            ");              } try {  object[] Parameters = GetParameters (targetinfo.parameters);                String data = TargetInfo.MethodInfo.Invoke (this, parameters) as String;            EndRequest (data); } catch (FormatException) {EndRequest ("parameter type matching error occurred!            "); } catch (InvalidCastException) {EndRequest ("parameter type conversion error occurred!            ");                } catch (ThreadAbortException) {} catch (Exception e) {            EndRequest (E.message); }}///<summary>///Get function metadata and cache///</summary>//<param name= "Methodn Ame "></param>//<returns></returns> private Cachemethodinfo trygetmethodinfo (string m Ethodname) {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>//Get function parameters///</summary>//<param name= "Parameterinfos" >& lt;/param>//<returns></returns> private object[] GetParameters (parameterinfo[] Parameterin FOS) {if (Parameterinfos = = NULL | | parameterinfos.length <= 0) {return n            Ull } 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 (); }    } 

Page class:

        [Ajaxmethod]        public string GetData3 (int i, double d, string str)        {            string[] datas = new string[] {i.tostring (), d.tostring (), STR};            Return "parameters are:" + string.join (",", datas);        }    

Front Code:

    function getData3 () {        $.ajax ({            headers:{"Ajaxflag": "XHR", "MethodName": "GetData3"},            data:{"I": 1, "D": "10.1a", "str": "Hehe"},            success:function (data) {                $ ("#result"). Text (data);            }        );    }

V. Summary

The above page base class already has the ability to complete basic functions, but it is not good enough. Mainly include:

1. Dependency on the page base class. For the original page base class, it will undoubtedly become more complex. We want to separate it into a separate component.

2. Efficiency issues. The efficiency of reflection is very low, especially in Web applications such as the use of caution. Taking the dynamic execution function as an example, the efficiency is mainly lower in:A. The process of dynamically locating a function based on a string. B. When the function is executed, the reflection interior needs to package the parameters into an array, then parse the parameters onto the thread stack, and before the call, the CLR also detects the correctness of the parameters and then determines if there is permission to execute. the above optimizations are only half optimized, that is, the process of searching is optimized, and invoke also has a performance penalty. Of course, the higher the version of the. NET, the more efficient the reflection will be, but this dynamic thing is always about efficiency in exchange for flexibility.

3. Complex parameters cannot be supported. Sometimes parameters are more, and function parameters are generally encapsulated into an object type.

4. AjaxMethodAttribute is just an empty tag attribute. We can add some functions to it, for example, the name of the tag function, whether to use the session, the cache settings, etc. can be done here.

Friends with WebForm may refer to the Ajaxpro component, which is an open source component, the next article through the source to understand the component, draw on its processing, and analyze its advantages and disadvantages.

Writing Lightweight AJAX Components 01-Compare the various implementations on the WebForm platform

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.