Objective
The previous article describes some of the ways to implement Ajax on the WebForm platform and implements a base class. This article we look at an open Source component: Ajaxpro. Although this is a relatively old component, but the realization of ideas and source code is still worth our learning. Through the introduction of the previous article, we know that the way to call the Page object is to rely on reflection to achieve, the key is the whole process, including the reflection call method, parameter mapping. Ajaxpro not only in the background to help us achieve this process, in the foreground also encapsulates the request call method, such as Ajax related methods, with Ajaxpro method can send asynchronous request, do not need to encapsulate JS or use JS library. The next step is to analyze the component.
First, the use of Ajaxpro
Let's take a look at how this component is used.
1. Register Ajaxhandlerfactory
The following configuration is done in web.config:
Simply put, the requested URL conforms to the AJAXPRO/*.ASHX format and is processed by Ajaxhandlerfactory, a factory class that implements the Ihandlerfactory interface for obtaining ihandler handlers. The format of the type is: "Name control. Class name, assembly name."
2. Register in the page class Page_Load event
protected void Page_Load (object sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax (typeof ( Ajaxpropage));
}
We have passed the type of this Page object to the Resistertypoforajax method, which is used to register the script in the foreground, specifically to call the current Page object's RegisterClientScriptBlock for registration, Therefore, there must be a <form runat= "server" ></form> in the. aspx file, or the script will not register. (The type is passed here, and it can actually be done without passing, internally through HttpContext.Current.Handler.GetType (). BaseType can also get this type)
3. Marking method with Ajaxmethod
[Ajaxmethod]
Public list<string> getlist (string input1,string input2)
{return
new list<string> {input1, input2 };
}
Ajaxmethod is a markup attribute that indicates that this method is used to handle AJAX requests, which are ultimately executed by reflection; It has several constructor pairs, and for some data that needs to be cached, you can set the cache time; If our request does not need to use session, You can set the Httpsessionstaterequirement, or you can set the handler to be asynchronous if the request requires asynchrony, such as requesting a time-consuming Web service.
The return value of a method can be either a simple type or a complex type, such as a collection type that is available in the foreground and is an array.
4. Front Call
Background configuration and use are very simple, then we see how the front desk to initiate the request.
function GetList () {
//var result = AjaxProNamespace.AjaxProPage.GetList ("A", "B"). Value;
Console.log (result);
AjaxProNamespace.AjaxProPage.GetList ("A", "B", function (Result) {
console.log (result);
});
Here Ajaxpronamespace is the namespace where the page class resides, Ajaxpropage is the name of the page class, and GetList is the method of tagging. Why do you write this? As mentioned earlier, Ajaxpro will register the script in the foreground, it will generate the following script according to the related information of our Page object, so we can call like this, and do not have to write JS ourselves or use the method of jquery library at all.
if (typeof ajaxpronamespace = = "undefined") ajaxpronamespace={};
if (typeof Ajaxpronamespace.ajaxpropage_class = = "undefined") ajaxpronamespace.ajaxpropage_class={};
Ajaxpronamespace.ajaxpropage_class = function () {};
Object.extend (AjaxProNamespace.AjaxProPage_class.prototype, Object.extend (New Ajaxpro.ajaxclass (), {
getlist: function (INPUT1, input2) {return
This.invoke ("GetList", {"input1": input1, "Input2": Input2}, this. Getlist.getarguments (). Slice (2));
},
URL: '/ajaxpro/ajaxpronamespace.ajaxpropage, Testajaxprosourcecode.ashx '
}));
Ajaxpronamespace.ajaxpropage = new Ajaxpronamespace.ajaxpropage_class ();
GetList parameters correspond to the parameters of the background method, the type must be convertible, or the call will fail. The last parameter is a callback function, and the parameter of the callback function is the object that encapsulates the returned result, and its Value property is the value of performing a successful return, such as an array object returned above. The error includes information about the failure.
Note that the part that is commented out above is the practice of synchronous requests, which are often not what we want, and I have seen people use them incorrectly.
Second, Ajaxpro processing request principle
The main focus here is on the process of component processing of AJAX requests, and other accessibility features are not introduced.
1. Generate Secondary scripts
In the Page_Load incident we called the AjaxPro.Utility.RegisterTypeForAjax (typeof (Ajaxpropage)); Used to register the required scripts. We noticed that the following script was introduced in the foreground page:
That is, every page will launch these requests. These are the ones. Ashx end of the file, but the actual inside are JS code; These JS some as a resource nested inside the DLL, some automatically generated, mainly encapsulate the AJAX request-related methods, as well as we can use: namespaces. The name of the page class. Tag method name this calls the method. Why use. ashx instead of. js? Because as a resource file inside a component, the. js file cannot be directly requested externally, and. Ashx can be intercepted, and then the content is exported with Response.Write.
If the efficiency of generating and sending these scripts is very low each time, ajaxpro internal processing is to judge the If-none-math and if-modified-since of the request headers, and if two are the same as the cache, return a 304 status code. As a result, the client only requests the server to return the contents of the file for the first request, followed by a return of only 304 indicating the use of the local cache. We refresh the page to verify the process:
2. Interception request
HttpHandler (IHttpHandler) and HttpModule (IHttpModule) are the ASP.net two important components, so we can easily expand on the basis of asp.net. HttpHandler corresponds to a specific request, such as. ashx,.aspx, HttpModule is an interceptor that can intercept all requests at an event in the pipeline. Simply put, in the pipeline, HttpApplication triggers a series of events that we register with HttpModule, for example, we can intercept the request before the handler object is generated, and then map it to our handler. While the actual processing request returns the result is HttpHandler, such as page used to generate HTML.
Taking the ASP.net MVC framework as an example, it is based on the ASP.net routing mechanism, the ASP.net routing system intercepts the request through a urlroutingmodule, specifically in the Postresolverequestcache event to intercept , the URL is parsed, the corresponding routing data is encapsulated, the request is finally handed to a mvchandler for processing, Mvchandler implements the IHttpHandler interface.
We made the following configuration: <add verb= "Post,get" path= "Ajaxpro/*.ashx" type= "ajaxpro.ajaxhandlerfactory, Ajaxpro"/> This shows any ajaxpro/arbitrary name. Ashx end of Post/get request, are handed to ajaxpro.ajaxhandlerfactory for processing, which is a ihandlerfactory processing factory that is used to generate specific IHttpHandler. Components internally define multiple implementations of IHttpHandler classes, some in order to generate JS script, for processing AJAX requests, mainly divided into two categories: asynchronous (IHttpAsyncHandler) and asynchronous (IHttpHandler), on the basis of these two categories, The support for the state of the session is divided into three types: support for reading and writing (implementation of irequiressessionstate tag interface) of Handler, Read-only (implements Ireadonlysessionstate markup interface) handler and handler that do not support session. What kind of handler is produced by Ajaxmethod is judged by the concrete.
The IHttpHandler ProcessRequest (asynchronous is BeginProcessRequest) is used to perform requests that return output results. We can also implement IHttpHandler if we need only one handler. The definition of ihandlerfactory is as follows:
Public interface IHttpHandlerFactory
{
IHttpHandler GetHandler (HttpContext context, String RequestType, String URL, string pathtranslated);
void Releasehandler (IHttpHandler handler);
}
Therefore, all AJAXPRO requests will conform to the AJAXPRO/*.ASHX format, and then the GetHandler method, you can do the specific processing, return the result is IHttpHandler; In the case of asynchronous states, if we configure a session to be required, Will generate a handler that implements IHttpHandler and IRequiresSessionState, and if you need a read-only session, A handler that implements IHttpHandler and Ireadonlysessionstate is generated, which can be obtained from the Ajaxmethod tag property through reflection. The main code for Ajaxhandlerfactory is as follows:
Public IHttpHandler GetHandler (HttpContext context, string RequestType, String url, String pathtranslated) {string file Name = Path.getfilenamewithoutextension (context.
Request.path);
Type t = null;
Exception typeexception = null;
BOOL Isintypeslist = false; Switch (RequestType) {//get request for the previous 4 script case ' get ': switch (filename.
ToLower ()) {case ' prototype ': Return to New Embeddedjavascripthandler ("prototype");
Case "core": Return new Embeddedjavascripthandler ("core");
Case "MS": Return new Embeddedjavascripthandler ("MS");
Case "Prototype-core": Case "Core-prototype": Return new Embeddedjavascripthandler ("Prototype,core");
Case "converter": Return to New Converterjavascripthandler ();
Default:return new Typejavascripthandler (t);
Case "POST": iajaxprocessor[] p = new iajaxprocessor[2];
P[0] = new Xmlhttprequestprocessor (context, t);
P[1] = new Iframeprocessor (context, t); for (int i = 0; i < P.lenGth i++) {if (P[i]. Canhandlerequest) {//Get the Ajaxmethod property of the markup method ajaxmethodattribute[] Ma = (ajaxmethodattribute[]) p[i].
Ajaxmethod.getcustomattributes (typeof (AjaxMethodAttribute), true);
BOOL Useasync = false;
Httpsessionstaterequirement sessionreq = Httpsessionstaterequirement.readwrite; if (MA. Length > 0) {useasync = ma[0].
useasyncprocessing; if (ma[0). Requiresessionstate!= httpsessionstaterequirement.usedefault) sessionreq = ma[0].
Requiresessionstate; //6 handler, depending on whether asynchronous, session state returns the specified handler switch (sessionreq) {case Httpsessionstaterequirement.rea
D:if (!useasync) return to New Ajaxsynchttphandlersessionreadonly (P[i]);
else return to New Ajaxasynchttphandlersessionreadonly (P[i]);
Case HttpSessionStateRequirement.ReadWrite:if (!useasync) return to New Ajaxsynchttphandlersession (P[i]);
else return to New Ajaxasynchttphandlersession (P[i]); Case HttpSessionStateRequirement.None:if (!useasync) return to New Ajaxsynchttphandler (P[i]);
else return to New Ajaxasynchttphandler (P[i]);
Default:if (!useasync) return to New Ajaxsynchttphandlersession (P[i]);
else return to New Ajaxasynchttphandlersession (P[i]);
}} break;
return null; }
3. Method of Reflection execution
When you obtain a handler that processes this request, you can execute the specified method in its ProcessRequest (asynchronous BeginProcessRequest). To execute a method of a Page object, we must know the assembly, namespace, name of the page class, and the name of the method that contains the specified page. This seems to fit in front of us: namespace. Class name. The method name is called. In order to be separate from the general request area and to have sufficient independence for the component, Ajaxpro only intercepts requests that conform to the AJAXPRO/*.ASHX format, which means that our Ajax request also conforms to this format. For example: HTTP://LOCALHOST:50712/AJAXPRO/AJAXPRONAMESPACE.AJAXPROPAGE,TESTAJAXPROSOURCECODE.ASHX, this format is automatically generated by the foreground script and does not need to be constructed. Careful observation, you will find Ajaxpronamespace.ajaxpropage,testajaxprosourcecode is the fully qualified name of the page class: the namespace. Class name, assembly name, through which we can generate a specific type and then reflect to get information. What about the name of the method? Ajaxpro it in the HTTP header, the name is: X-ajaxpro-method. With this information, you can reflect the execution method. Here the core code is:
internal void Run () {try {//Set output not cached (this is not necessarily what we want) p.context.response.expires = 0;
P.context.response.cache.setcacheability (System.Web.HttpCacheability.NoCache);
P.context.response.contenttype = P.contenttype;
p.context.response.contentencoding = System.Text.Encoding.UTF8; Verify that the AJAX request if (!p.isvalidajaxtoken ()) {P.serializeobject (The new System.Security.SecurityException ("The Ajaxpro-token
is not valid. "));
Return
}//method Parameter Object array object[] po = null;
Request Processing Result Object res = NULL;
try {//get parameter PO = p.retreiveparameters (); The catch (Exception ex) {}//Get the cached key string cachekey = P.type.fullname + "|" + P.gettype ().
Name + "|" + P.ajaxmethod.name + "|" + P.gethashcode (); if (P.context.cache[cachekey]!= null) {//If the cache exists, use the cache P.context.response.addheader directly ("x" + Constant.ajaxid + "-
Cache "," Server ");
P.context.response.write (P.context.cache[cachekey]);
Return try {if (p.ajaxmethod.isstatic) {//Invoke static method with reflection TRy {res = P.type.invokemember (p.ajaxmethod.name, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.InvokeMethod, NULL, NULL, PO); ' Catch (Exception ex) {}} ' else {try {///Create instance object, reflection invoke instance Method Object c = (object) Activator.crea
Teinstance (P.type, new object[] {});
if (c!= null) {res = P.ajaxmethod.invoke (c, PO); The catch (Exception ex) {}} (Exception ex) {} try {//judged result is not XML, setting contenttype if (res != null && Res.
GetType () = = typeof (System.Xml.XmlDocument)) {P.context.response.contenttype = "text/xml";
p.context.response.contentencoding = System.Text.Encoding.UTF8; ((System.Xml.XmlDocument) res).
Save (P.context.response.outputstream);
Return
string result = null;;;
System.Text.StringBuilder sb = new System.Text.StringBuilder ();
try {result = P.serializeobject (res); A catch (Exception ex) {}//If caching is required, writes the result to the cache if (P.servercacheattributes.length > 0) {if (P.SERVERCAC Heattributes[0]. iscacheenabled) {P.context.cache.add (CacheKey, result, NULL, DATETIME.NOW.ADD (p.servercacheattributes[0).
CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, NULL);
catch (Exception ex) (Exception ex) {}}
Third, summary
Let's summarize the core process of Ajaxpro, which intercepts the URL in the specified format through a ihttphandlerfactory and then gets the fully qualified name of the type to generate the object, and then gets the attribute of the markup method through reflection. Generates a custom object that implements the IHttpHandler interface, in its ProcessRequest method, gets the method name from the HTTP headers, maps the parameters through reflection, and executes the function.
Ajaxpro has the following advantages:
1. Simple configuration.
2. Can be used in conjunction with other components.
3. Encapsulate the foreground script, we don't have to encapsulate it ourselves or use other script libraries.
4. For return value processing, we can return a simple type or a complex type will be automatically serialized.
Disadvantages are:
1. The page will have 4 more requests. Although the 304 cache is used, the request needs to be sent to the server.
2. Ajax cannot use GET requests. Because of the custom URL format, the use of this format can not use the GET request, we know that the GET request is cached by the browser, Yahoo front-end optimization proposal is one of the most use GET request. In fact, you should place the namespace. Class name, assembly in the HTTP header, and then provide a type of parameter that allows us to choose freely.
3. With <form runat= "Server" > Bindings. The goal is to generate foreground script for us, but if we want to use the. html file +. Aspx.cs, it's not going to work (this is the way the blog has some pages); Even our interface may have to be used on the mobile side, and this convenience becomes a limitation.
4. Reflection. This efficiency is relatively low, and it does not even cache the MethodInfo as we did in the previous page class.
It can be seen that this component is worth using if you are not too fussy about efficiency. Here is just a core introduction, there are many other functions, this is the source code of Ajaxpro components, interested friends can study.