Compile lightweight ajax components 01-comparison with various implementation methods on the webform platform, 01-webform
Preface
Asp.net WebForm and Asp.net MVC (MVC) are both web development frameworks based on Asp.net. There is a big difference between them. One of them is that MVC pays more attention to the http nature, while WebForm tries to block http, therefore, a large number of server controls and ViewState mechanisms are provided, allowing developers to program based on the event model just like developing a Windows Form application. The two have their own advantages and disadvantages and applicable scenarios, but MVC is now the first choice for many Asp.net developers.
WebForm is built on Asp.net. Asp.net provides sufficient scalability. We can also use these frameworks to write MVC-like frameworks under WebForm. This gives us the opportunity to write more. When it comes to WebForm, many people will think of server controls (drag controls !!!), In fact, we may not use server controls at all, and pay attention to html like MVC. To discard server controls and focus on html, WebForm first removes the <form runat = "server"> </form> label. This runat server form is the basis of its PostBack mechanism. Since we want to return to html + css + js, it means that many things must be implemented by ourselves, such as processing Ajax requests. Unlike MVC, WebForm begins to take the server control as its main component. If it is not used, it can only be implemented with its scalability.
This series is a lightweight ajax component based on the WebForm platform. It consists of three parts:
1. describes various implementation methods in WebForm.
2. Analyze the ajaxpro component.
3. compile your own ajax components.
I. Ajax Introduction
Asynchronization allows us to request or submit data on a server without refreshing the entire page. For complex pages, reloading the entire page to request a little data is obviously inefficient. ajax is designed to solve this problem. The core of ajax is the XmlHttpRequest object, which submits requests to the server in the form of text. After XmlHttpRequest2.0, binary data can be submitted.
Ajax security: For security reasons, ajax is restricted by the same-origin policy. That is, requests that can only access the same domain and the same port are rejected. Of course, sometimes cross-origin requests are required. common cross-origin processing methods include CORS (cross-origin resource sharing) and JSONP (JSON ).
Ajax Data Interaction format: although the Ajax core object XmlHttpRequest has the word "XML", the format of data exchange between the client and the server is not limited to xml. For example, the json format is more used.
Ajax also has shortcomings. For example, the support for search engines is not very good, and sometimes it violates the original intention of url resource positioning.
Ii. Use ajax on the Asp.net MVC Platform
In MVC, ajax is very convenient to call the background method. You only need to specify the Action name.
Front-end code:
<body>
Background code:
public class AjaxController : Controller{ public ActionResult GetData() { if(Request.IsAjaxRequest()) { return Content("data"); } return View(); }}
3. Use ajax on the WebForm Platform
3.1 server control package or third-party components
This is based on server controls, such as the ajax toolkit or components such as FineUI. Web Front-end is always composed of html + css + js, but how to generate it. Native, we can write it by ourselves or use some front-end plug-ins. server-based controls are all generated in the background, which is generally less efficient. The server component will generate a series of proxies at the front end, which are essentially the same, but the control encapsulates this process and does not need to be compiled by ourselves. Based on controls or third-party components, it is useful in some management systems and can be developed quickly because of a low access volume.
3.2 ICallbackEventHandler-based interface
. Net provides the ICallbackEventHandler interface for processing callback requests. This interface needs to use ClientScriptManager to generate a proxy script on the foreground to send and receive requests. Therefore, the <form runat = "server"> label is required.
Front-end 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 request initiating script. callServer is the execution function string scriptExecutor = "function callServer () of the button event () {"+ functionName +" ;}"; // register the script scriptMgr. registerClientScriptBlock (this. getType (), "callServer", scriptExecutor, true);} // interface method public string GetCallbackResult () {return "callback result ";} // interface method public void RaiseCallbackEvent (string eventArgument ){}}
This method has the following Disadvantages::
1. The implementation is complicated. Every page Load event needs to register the corresponding script.
2. the foreground will generate a script file for proxy.
3. Complicated page interaction makes implementation very troublesome.
4. Although it is a callback, the page object is still generated.
3.3 Use a general Handler
Generally, a processing program is actually an IHttpHandler interface class. Like a page class, it can also be used to process requests. Generally, the handler is not used to generate html, nor is there a complex event mechanism. There is only one ProcessRequest entry for processing requests. We can write the ajax request address as the path of the. ashx file, which can be processed with high efficiency.
To output text content, you only need Response. Write (data). For example, after obtaining data from the database, serialize it to a json string and then output it. As mentioned above, generally, the handler does not generate html like the previous one on the page. If you want to generate html, you can generate it by loading the user control. For example:
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); }}
This method is lightweight and efficient. The disadvantage is that many ashx files need to be defined for interaction, which increases management and maintenance costs.
3.4 Page Base Class
Define the method for processing ajax requests in the page object, so that each page can focus on processing requests related to this page. Note the following.
1. How do I know that this request is an ajax request?
The request X-Requested-With: XMLHttlRequest indicates that most asynchronous requests in browsers contain this request header. You can also customize the request header, for example, AjaxFlag: XHR.
2. Where should I handle it in a unified manner?
It is troublesome to judge and call in each page class, so the process is transferred to a page base class for processing.
3. How do I know which method to call?
You can either pass the parameter or define it in the Request Header, for example, MethodName: GetData.
4. I know the method name. How can I call it dynamically?
Reflection.
5. How can I know that this method can be called externally?
Public type can be considered as an external call, or an attribute tag can be marked.
Through the above analysis, the simple implementation is as follows:
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 blank! ");} Type type = this. getType (). baseType; MethodInfo info = type. getMethod (methodName, BindingFlags. public | BindingFlags. instance | BindingFlags. static); if (info = null) {EndRequest ("no suitable method call 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 type:
public partial class Test1 : PageBase{ protected void Page_Load(object sender, EventArgs e) { } public string GetData() { return "213"; }}
Front-end code:
function getData(){ $.ajax({ headers:{"AjaxFlag":"XHR","MethodName":"GetData"}, success:function(data){ $("#result").text(data); } });}
4. Optimized Page Base
The above page base class has few functions, and the call efficiency through reflection is very low. Here we will optimize it:
1. Simple parameters are supported.
For example, the above GetData can be: GetData (string name). Relevant parameters can be obtained through function metadata, and then the parameters can be set based on the request parameters.
2. Add the tag attribute.
Only attributes marked by AjaxMethodAttribute can be called externally.
3. Optimized reflection.
Use the cache to avoid searching function information based on the function name every time.
Tag attributes:
public class AjaxMethodAttribute : Attribute{}
Cache object:
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> /// <par Am name = "methodName"> </param> private void InvokeMethod (string methodName) {if (string. IsNullOrEmpty (methodName) {EndRequest ("MethodName mark cannot be blank! ");} CacheMethodInfo targetInfo = TryGetMethodInfo (methodName); if (targetInfo = null) {EndRequest (" an 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 match error! ");} Catch (InvalidCastException) {EndRequest (" An error occurred while converting the parameter type! ");} Catch (ThreadAbortException) {} catch (Exception e) {EndRequest (e. message );}} /// <summary> /// obtain the function metadata and cache it /// </summary> /// <param name = "methodName"> </param> // <returns> </returns> private CacheMethodInfo TryGetMethodInfo (string methodName) {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 (ajaxmethodattrites), 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> /// obtain the function parameters /// </summary> /// <param name = "parameterInfos"> </param> /// <returns> </returns> priv Ate object [] GetParameters (ParameterInfo [] parameterInfos) {if (parameterInfos = null | parameterInfos. length <= 0) {return null;} 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 type:
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-end 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 basic functions, but it is not good enough. Mainly include:
1. attach to the page base class. Page Base classes will undoubtedly become more complex. We want to separate it into a separate component.
2. efficiency issues. Reflection efficiency is very low, especially in applications such as web, should be used with caution. Taking dynamic function execution as an example, the efficiency is mainly lower: a. The process of dynamically searching functions based on strings. B. when executing a function, the reflection needs to package the parameters into an array and resolve the parameters to the thread stack. before calling the function, the CLR must check the correctness of the parameters and determine whether there is any permission for execution. In fact, the above optimization is only half optimized, that is, the search process is optimized, and Invoke will also suffer performance loss. Of course, the higher the. net version, the higher the reflection efficiency, but this dynamic thing always exchanges efficiency for flexibility.
3. complex parameters are not supported. Sometimes there are many parameters, and function parameters are generally encapsulated into an object type.
4. AjaxMethodAttribute is only an empty tag attribute. We can add some functions for it, such as marking the function name, using Session, and caching settings.
A friend who has used WebForm may mention the AjaxPro component, which is an open-source component. The next article will learn about this component through the source code, learn from its processing process, and analyze its advantages and disadvantages.