Write lightweight Ajax Component 01-_ajax related to various implementations on the WebForm platform

Source: Internet
Author: User
Tags httpcontext reflection

Objective

asp.net webform and asp.net mvc (MVC for short) are based on the ASP.net web development framework, one of the big differences is that MVC focuses more on the HTTP nature, and WebForm tries to block HTTP by providing a number of server controls and viewstate mechanisms that allow developers to develop windows Form applications, programming based on the event model. Both have pros and cons and apply scenarios, but MVC is now the first choice for many asp.net developers.

WebForm is based on ASP.net, asp.net provides enough extensibility, we can also use these to write a framework like MVC under WebForm, which has the opportunity to write again. When it comes to WebForm, many people associate with server controls (drag controls!!! In fact, we can do without server controls at all, and look at HTML as MVC does. WebForm to discard server controls and focus on HTML, the first step is to remove <form runat= "server" ></form> tags, the 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 processing AJAX requests. Unlike MVC, WebForm begins with a server control as a primary component, and if it is not used, it can only be implemented with its extensibility.

  This series is the implementation of a WebForm platform based lightweight AJAX components, mainly divided into three parts:

1. Introduce the various ways of implementation under WebForm.

2. Analyze Ajaxpro components.

3. Write your own AJAX components.

Introduction of Ajax

Asynchronously allows us to request or submit data like a server without refreshing the entire page. For complex pages, it is obviously inefficient to overload the entire page in order to request a bit of data, Ajax is to solve this problem. The core of Ajax is the XMLHttpRequest object, through which the request is submitted to the server in the form of text. XmlHttpRequest2.0 also supports the submission of binary data.

Ajax security: For security reasons, Ajax is subject to the same-source policy, that is, requests that can only access the same domain, the same port, and cross domain requests are rejected. Of course, sometimes requirements need to be sent across domain requests, and common cross-domain processing methods include Cors (cross-domain resource sharing) and JSONP (parametric JSON).

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

Ajax has its drawbacks, too. For example, the search engine support is not very good, sometimes it will violate the original purpose of URL resource positioning.

Second, the ASP.net MVC platform to use Ajax

In MVC, Ajax calls the backend method very handy, just specify the name of the action.

Foreground code:

<body>
   
 

Background code:

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

Three, WebForm platform to use Ajax

  3.1 Based on server control packs or third party components

This is based on a server control, such as an AJAX Toolkit toolkit, or a component like Fineui. The Web front-end is always made up of html+css+js, just how to generate the problem. Native we can write it ourselves, or use some front-end plug-ins; 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 ourselves. Based on the control or third-party components of the model, in some management system is very useful, the traffic is not very large, can be quickly developed.

  3.2 Based on ICallbackEventHandler interface

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

Foreground 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;
 
    Gets 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. The implementation is more complex, each page load event to register the corresponding script.

2. The foreground will generate a script file for the agent.

3. For the page interaction complex, to achieve a very cumbersome.

4. Although it is a callback, the Page object is still generated.

3.3 Using a generic handler

The general handler is actually a IHttpHandler interface class that, like the page class, can also be used to process requests. Generic handlers are typically not 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 highly efficient.

To output text content, you need only Response.Write (data), for example, after you have obtained from the database, serialize to the JSON format string, and then output. As mentioned earlier, the generic handler does not generate HTML as it was on the page, and if you want to generate HTML, you can build it 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 lightweight and efficient, and the disadvantage is that many ashx files are defined for more interactive needs, increasing the cost of management and maintenance.

  3.4 Page Base class

The method of processing AJAX requests is defined within the Page object, so that each page can focus on the requests related to this page. Here's something to be aware of.

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

By requesting x-requested-with:xmlhttlrequest, it can be judged that most of the browser's asynchronous requests will contain the request header, or it can be implemented through a custom request header, for example: AJAXFLAG:XHR.

  2. Where is the unified treatment?

If it's cumbersome to judge and invoke in each page class, move the process to a page base class.

 3. How do I know which method is invoked?

It can be done by passing parameters or by defining them in the request header, for example: Methodname:getdata.

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

Reflection.

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

You can think of the public type as being called externally, or by marking the property tag.

Through the above analysis, the 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 ("Unable to find the appropriate method call!") ");
      }        
      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 ';
  }
}

Foreground code:

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

Iv. Optimized page base class

The page base class above has very little functionality and is inefficient to invoke through reflection. Here to optimize:

1. Can support simple types of parameters.

For example, the above GetData can be: GetData (string name), through the function metadata can get the relevant parameters, and then based on the requested parameters, you can set the parameters.

2. Add tag attributes.

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

3. Optimize reflection.

Use caching to avoid searching for function information each time based on the function name.

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>///<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 ("Cannot find the appropriate method call!")
    ");
      try {object[] parameters = GetParameters (targetinfo.parameters);
      String data = TargetInfo.MethodInfo.Invoke (this, parameters) as String;
    endrequest (data); catch (FormatException)
    {endrequest ("parameter type match error!")
    "); The catch (InvalidCastException) {endrequest ("parameter type conversion has an error!")
    ");
    catch (ThreadAbortException) {} catch (Exception e) {endrequest (e.message);
  }///<summary>///get function metadata and cache///</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 (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" ></param>/// <returns></returns> Private object[] GetParameters (parameterinfo[] parameterinfos) {if (ParameterInfo s = = 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 class:

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

Foreground 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 perform basic functions, but it's not good enough. The main are:

1. Attached to the base class of the page. For the original page base class, it will undoubtedly become more complex. We want to make it independent and become a separate component.

2. Efficiency issues. The efficiency of reflection is very low, especially in applications such as the Web, should be used with caution. Take the dynamic execution function as an example, the efficiency is mainly low in: a. The process of dynamically locating a function from a string. B. When executing a function, the reflection internally needs to package the parameters into an array, then parse the parameters onto the thread stack, before invoking the CLR to detect the correctness of the parameters, and then to determine if there is permission to execute. The above optimization is only half optimized, that is, optimizing the search process, and invoke also has performance loss. Of course, the higher the. NET version, the greater the efficiency of reflection, but this dynamic thing is always in exchange for flexibility in efficiency.

3. Cannot support complex parameters. Sometimes the parameters are more and the function parameters are usually encapsulated into an object type.

4. AjaxMethodAttribute is just an empty tag attribute. We can add some functionality to it, such as the name of the tag function, whether to use session, cache settings, and so on.

Friends who have used WebForm may refer to the Ajaxpro component, which is an open source component, and the next article understands the component through the source code, borrows its processing and analyzes its pros and cons.

Related Article

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.