Deep into the Atlas series server-side support (top)

Source: Internet
Author: User
Tags bool config extend http request httpcontext interface variable web services
Server

In the previous article, we analyzed the client code used for AJAX access to Web services using Atlas. However, if you want to achieve this function, it is clear that the server-side support. In this article, we will discuss this point.

Adding server-side support is actually adding/changing the way to process an HTTP request. In ASP.net, the request is handled through a class that implements the System.Web.IHttpHandler interface. In web.config, we can map the request to the class that implements IHttpHandler by configuring it to tell ASP.net who will handle the request. For example, in Atlas, the file request is atlasglob.axd to the Microsoft.Web.Globalization.GlobalizationHandler class for the culture supporting file.

<add verb= "*" path= "Atlasglob.axd" type= "Microsoft.Web.Globalization.GlobalizationHandler" validate= "false"/>

Taking advantage of this in Atlas, changing the way you handle *.ASMX requests requires special handling for requests with MN in query string (I will mention in a future article that there is another way of handling the request for "*.asmx/js".) It provides a proxy for client access to Web services, which is beyond the scope of this article. As a result, if you need to use Atlas to access Web Services from the client in Ajax, the following settings are absolutely essential in web.config:

<remove verb= "*" path= "*.asmx"/>
<add verb= "*" path= "*.asmx" type= "Microsoft.Web.Services.ScriptHandlerFactory" validate= "false"/>
This setting removes the mapping of the original *.asmx file request and *.asmx the file request to Microsoft.Web.Services.ScriptHandlerFactory. This is the support of Atlas on the server side.

The next step is to start analyzing the code in the Microsoft.Web.Atlas.dll provided by Atlas. The amount of code and complexity in this assembly is much greater than the client code in atlas. Therefore, I only for the key role of the code for detailed analysis, some auxiliary methods or classes of implementation, only interested friends to see for themselves. In addition, for everyone to read conveniently, I changed the local variable name to a higher readability of the names, to avoid the "Text1", "Flag1" such as variable name, I hope to read the code to help.

Let's take a look at the members of the Microsoft.Web.Services.ScriptHandlerFactory class:

ScriptHandlerFactory Class Members:

1 public class Scripthandlerfactory:ihttphandlerfactory
2 {
3//Methods
4 public scripthandlerfactory ();
5 private static void checkatlaswebservicesenabled ();
6 Public virtual IHttpHandler GetHandler (HttpContext context, string RequestType, String url, string pathtranslated);
7 Public virtual void Releasehandler (IHttpHandler handler);
8
9//Fields
a private ihttphandlerfactory _resthandlerfactory;
One private ihttphandlerfactory _webservicehandlerfactory;
12
//Nested Types
Private class AsyncHandlerWrapper:ScriptHandlerFactory.HandlerWrapper, IHttpAsyncHandler, IHttpHandler
15 {
//Methods
Internal Asynchandlerwrapper (IHttpHandler Originalhandler, IHttpHandlerFactory originalfactory);
IAsyncResult Public BeginProcessRequest (HttpContext context, AsyncCallback cb, Object Extradata);
public void EndProcessRequest (IAsyncResult result);
20}
21st
Private Class AsyncHandlerWrapperWithSession:ScriptHandlerFactory.AsyncHandlerWrapper, IRequiresSessionState
23 {
Methods//
Internal Asynchandlerwrapperwithsession (IHttpHandler Originalhandler, IHttpHandlerFactory originalFactory);
26}
27
Internal class Handlerwrapper:ihttphandler
29 {
/Methods
Internal Handlerwrapper (IHttpHandler Originalhandler, IHttpHandlerFactory originalfactory);
The public void ProcessRequest (HttpContext context);
internal void Releasehandler ();
34
/Properties
public bool IsReusable {get;}
37
Fields//
The private ihttphandlerfactory _originalfactory;
protected IHttpHandler _originalhandler;
41}
42
Internal class HandlerWrapperWithSession:ScriptHandlerFactory.HandlerWrapper, IRequiresSessionState
44 {
Methods//
Internal Handlerwrapperwithsession (IHttpHandler Originalhandler, IHttpHandlerFactory originalfactory);
47}
48}
As you can see, in addition to the method of IHttpHandlerFactory interface, there are "rich" members in the interior of the class. The checkatlaswebservicesenabled () static method is to see whether to provide server-side support for the Atlas access webservices, or to throw an exception if not supported. To enable Atlas to provide support for the server side, the following elements need to be added to the web.config:

<microsoft.web>
<webservices enablebrowseraccess= "true"/>
</microsoft.web>
In addition, within the ScriptHandlerFactory class, there are several internal classes that provide a simple encapsulation of the IHttpHandler object. Using such a wrapper class in your own code is a common way to extend an existing framework. By reading Microsoft.Web.Atlas.dll's code, you can find that the next to HttpRequest in Atlas, up to page, provides a large and small dozen wrapper classes.

We look at the ScriptHandlerFactory constructor:

ScriptHandlerFactory constructor:

1 public scripthandlerfactory ()
2 {
3 this._resthandlerfactory = new Resthandlerfactory ();
4 this._webservicehandlerfactory = new Webservicehandlerfactory ();
5}
The constructor is fairly simple, but initializes the two private fields of the class. ScriptHandlerFactory, when working, will generate and release ihttphander objects of responsibility, according to a certain logic entrusted to the two IHttpHandlerFactory class object one. This._resthandlerfactory is an instance of the Microsoft.Web.Services.RestHandlerFactory class that handles the expansion of Atlas for *.asmx requests. And This._webservicehandlerfactory is an instance of the System.Web.Services.Protocols.WebServiceHandlerFactory class, so what is it? A file is known as "%windows%\microsoft.net\framework\v2.0.50727\config\web.cofig", which provides a asp.net global default configuration. We can find such a setting inside:

......
<add path= "*.asmx" verb= "type=" System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, version=2.0.0.0, Culture=neutral, publickeytoken=b03f5f7f11d50a3a "validate=" False "/"
......

It can be found that this is the ASP.net original processing *.asmx request of the class.   The expansion of Atlas ensures that the existing functionality is not compromised, and therefore uses this class to process requests outside of the extension. The next step into the IHttpHandlerFactory key method: GetHandler. The code is as follows:

GetHandler Method Analysis:

1 public virtual IHttpHandler GetHandler (HttpContext context, string RequestType, String url, string pathtranslated)
0 9
3 IHttpHandlerFactory Factory;
4
5//Determine if the Atlas extension request
6 if (resthandlerfactory.isrestrequest (context))
7 {
8//Check if support for Atlas Access Web Services is available
9 scripthandlerfactory.checkatlaswebservicesenabled ();
10//entrusted to resthandlerfactory for processing
One factory = This._resthandlerfactory;
12}
Or else
14 {
15//Since it is not an atlas extension request, it is processed using the ASP.net original method
Factory = This._webservicehandlerfactory;
17}
18
19//Call the factory GetHandler method to obtain the handler to process the request
IHttpHandler handler = Factory. GetHandler (context, RequestType, url, pathtranslated);
21st
22//The following code is based on whether handler support session,
23//And whether it is asynchronous handler, select a different wrapper class
24//For encapsulation and return.
BOOL Requiressession = handler is irequiressessionstate;
-if (handler is IHttpAsyncHandler)
27 {
if (requiressession)
29 {
Return to new Scripthandlerfactory.asynchandlerwrapperwithsession (handler, factory);
31}
Return to new Scripthandlerfactory.asynchandlerwrapper (handler, factory);
33}
if (requiressession)
35 {
Return to new Scripthandlerfactory.handlerwrapperwithsession (handler, factory);
37}
Return to new Scripthandlerfactory.handlerwrapper (handler, factory);
39}
The four wrapper classes are scripthandlerfactory.handlerwrapper and their subclasses. The reason why so many packaging classes, is in order to carry out unified packaging, while preserving the original function of the asp.net unchanged. With a unified package, ScriptHandlerFactory Releasehandler can also be very easy to deal with, just entrust the responsibility to handler itself, be divided into the handler itself can know what factory they should use to release their own. The code is as follows:

Releasehandler Code:

1 public virtual void Releasehandler (IHttpHandler handler)
0 9
3 if (handler = null)
4 {
5 throw new ArgumentNullException ("handler");
6}
7 ((Scripthandlerfactory.handlerwrapper) handler). Releasehandler ();
8}
The next thing to care about is the Resthandlerfactory class of GetHandler method, the code is as follows:

Analysis of the GetHandler method of Resthanderfactory:

1 public virtual IHttpHandler GetHandler (HttpContext context, string RequestType, String url, string pathtranslated)
2 {
3//If the request is "*.asmx/js", then the Web Services agent is required
4 if (resthandlerfactory.isclientproxyrequest (context). Request.pathinfo))
5 {
6//Then return the processing agent handler
7 return new Restclientproxyhandler ();
8}
9
10//Use static function Createhandler get handler.
One return to Resthandler.createhandler (context);
12}
The request for the Web Services agent in Atlas will be discussed in a future article, and now we are only concerned with the behavior of the Resthandler static method Createhandler (HttpContext). The code is as follows:

Createhandler (HttpContext) method Analysis:

1 internal static IHttpHandler Createhandler (HttpContext context)
2 {
3//Using Webservicedata static method Getwebservicedata (string) to obtain the Webservicedata object,
4//It describes the information for the Web services that will be used.
5 Webservicedata data = Webservicedata.getwebservicedata (context. Request.path);
6//Get method Name
7 String methodname = Context. Request.querystring["MN"];
8//Using Createhandler (Webservicedata, String) to obtain handler
9 return Resthandler.createhandler (data, MethodName);
10}
A very important class appears here, Webservicedata, which encapsulates web Services accessed through Atlas and provides important features such as caching. This class can be said to be an important part of Atlas's access to Web Serivces, and I'll do a simple analysis of it next. To be realistic, understanding these codes (and even analysis of the entire server-side code) does not have a direct effect on the ability to use the Atlas technology, so friends who are not interested can skip this section and look directly at the conclusions and examples that follow. :)

Before parsing Webservicedata.getwebservicedata (string), let's look at the static constructor for this class.

Webservicedata Static constructor Analysis:

1 static Webservicedata ()
0 9
3//Cache: The Web Services type is used as the Key,webservicedata instance as value
4 Webservicedata._cache = hashtable.synchronized (New Hashtable ());
5//Cache: Use the virtual path of the *.asmx file as the type of the Key,web service as value
6 webservicedata._mappings = hashtable.synchronized (new Hashtable (stringcomparer.ordinalignorecase));
7//Cache: In contrast to the above, use type as key,virtual path as value
8 Webservicedata._typevirtualpath = hashtable.synchronized (new Hashtable (stringcomparer.ordinalignorecase));
3 ·
The function of a static constructor is to initialize each cache object, and Atlas uses the synchronized Hashtable as a container for the cache. It seems regrettable that the. NET Framework 2.0 did not provide synchronized Generic Collection.

The next thing to look at is the static method Getwebservicedata (string), but it simply uses the static method Getwebservicedata (string, BOOL) and sets the second argument to true, so we skip it, See the implementation of static method Getwebservicedata (string bool) directly. The code is as follows:

Getwebservicedata Method Analysis:

1 internal static Webservicedata Getwebservicedata (string virtualpath, bool failifnodata)
0 9
3 if (Virtualpath.endswith ("Bridge.axd", Stringcomparison.invariantcultureignorecase))
4 {
5 virtualpath = virtualpath.substring (0, virtualpath.length-10) + ". Asbx";
6}
7
8//Get absolute path (~/XXXXX/XXX.XXX)
9 virtualpath = Virtualpathutility.toabsolute (virtualpath);
10//Try to get the type of Web service from cache
One type Wstype = Webservicedata._mappings[virtualpath] as Type;
12
BOOL wsfileexists = false;
14//If there is no in the cache
if (Wstype = null)
16 {
17//See if the Web service file you are accessing exists
Wsfileexists = HostingEnvironment.VirtualPathProvider.FileExists (virtualpath);
19//If present
if (wsfileexists)
21 {
22//Compile the Web service file and get its type
Wstype = Buildmanager.getcompiledtype (virtualpath);
24}
25}
26
27//If no type is obtained and the Web Services file does not exist,
28//Note that this is not a user-provided web service, but rather a use of an assembly
29//The type that you provide, so you are looking in the assembly.
if ((Wstype = null) &&!wsfileexists)
31 {
string typeName = null;
int num1 = Virtualpath.indexof ("scriptservices/");
34//If there are scriptservices/in the path
if (NUM1!=-1)
36 {
Panax NUM1 = "scriptservices/". Length;
38//Intercept the string after "scriptservices/" and remove the extension
TypeName = Virtualpath.substring (NUM1, (VIRTUALPATH.LENGTH-NUM1)-5);
40//Convert all '/' to '. ', which becomes a fullname of the class.
TypeName = Typename.replace ('/', '. ');
42//This type is obtained from the Assembly of Atlas itself.
Wstype = typeof (Webservicedata). Assembly.GetType (TypeName, False, true);
44//If this type is not in the Atlas assembly, look for this type globally
if (Wstype = null)
46 {
Wstype = BuildManager.GetType (TypeName, False, true);
48}
49}
Or else
51 {
Try
53 {
54//Remove Extension
TypeName = Path.getfilenamewithoutextension (virtualpath);
56//Use reflection to invoke Sys.Web.UI.Page decryptstring to get TypeName
TypeName = webservicedata.decryptstring (typeName);
Wstype = Type.GetType (typeName);
59}
Catch
61 {
62}
63
if (Wstype!= null)
65 {
66//In cache, save the corresponding relationship between type object and virtual path.
Webservicedata._mappings[virtualpath] = Wstype;
Webservicedata._typevirtualpath[wstype] = virtualpath;
69}
70}
71}
72
73//If you get the type of the Web service
if (Wstype!= null)
75 {
76//Webservicedata object is obtained by static method Getwebservicedata (Type)
Webservicedata.getwebservicedata return (Wstype);
78}
79
if (Failifnodata)
81 {
throw new InvalidOperationException ();
83}
84
return null;
86}
method is used within the methods provided by part of the. NET Framework 2.0, please refer to MSDN If you want to be specific about these methods.

This is a more complicated approach, but reading it can make Atlas a new step in the way it is used. The above code has been commented, it should be more convenient to understand. It may be necessary for me to elaborate on the specific meaning of lines 35th through 49th here. The logical purpose of this is to map a path to a type, and the current application in Atlas is to actually request a path when using the authentication service scriptservices/microsoft/web/ Services/standard/authenticationwebservice.asmx ", naturally in the client will not have this file. The path is then removed from the extensions and scriptservices, into "Microsoft/web/services/standard/authenticationwebservice" and All "/" into "." becomes the identity "Microsoft.Web.Services.Standard.AuthenticationWebService" of a class that you can find in the assembly. The same approach, which is also present in the Profile service of Atlas, requests Web Services that are "scriptservices/microsoft/web/services/standard/ Profilewebservice.asmx ".

For developers, the value is that we can compile the request processing of a Web service method in the assembly! For example, we just need to write a Jeffz.Atlas.SampleService class to inherit System.Web.Services.WebService and request in JavaScript "scriptservices/jeffz/ Atlas/sampleservice.asmx "can be. This provides a great convenience for publishing and deploying builds, and for friends who like to write Extender, it also provides the perfect way to interact with the server side. In this connection, I will give a specific example for your reference in this series of articles.

Continuing back to the analysis of the Code, Getwebservicedata (string, BOOL) eventually returned the result of the Getwebservicedata (Type) call. The code is as follows:

Getwebservicedata (Type) method Analysis:

1 private static Webservicedata Getwebservicedata (type type)
2 {
3//try to get Webservicedata object from cache
4 Webservicedata data = Webservicedata._cache[type] as Webservicedata;
5
6//If there is no in the cache
7 if (data = null)
8 {
9//Construct the object
data = new Webservicedata (type);
11//and put in cache
Webservicedata._cache[type] = data;
13}
14
return data;
16}
The code is simple enough to explain it. The constructor of the Webservicedata class does not need parsing, but simply preserves that type. The code is as follows:

Webservicedata constructor:

1 private Webservicedata (type type)
2 {
3 This._type = type;
4}
The analysis of the Webservicedata class comes here first, and we go back to the previous code. To get the IHttpHandler object is to invoke the Resthandler Createhandler (Webservicedata, String) static method with the following code:

Createhandler (Webservicedata, String) static method analysis:

1 private static IHttpHandler Createhandler (Webservicedata webservicedata, String methodname)
2 {
3 Resthandler handler;
4//Call Getmethoddata Get Webservicemethoddata object instance,
5//Describes a Web service method.
6 Webservicemethoddata data = Webservicedata.getmethoddata (methodname);
7
8//depending on whether to support session selection of different handler
9 if (data. Requiressession)
10 {
One handler = new resthandlerwithsession ();
12}
Or else
14 {
Handler = new Resthandler ();
16}
17
Handler._webservicemethoddata = data;
return handler;
20}
Here comes the description class Webservicemethoddata for Web services methods, which are obtained through the Webservicedata Getmethoddata method. The method code is as follows:

Getmethoddata Method Analysis:

1 internal webservicemethoddata Getmethoddata (string methodname)
2 {
3//Ensure that the description of method is loaded and saved
4 this. Ensuremethods ();
5
6 Webservicemethoddata data = This._methods[methodname];
7 if (data = null)
8 {
9 throw new ArgumentException (string. Format (CultureInfo.CurrentCulture, Atlasweb.unknownwebmethod, new object[] {methodname}), "MethodName");
10}
11
return data;
13}
This. The Ensuremethod () method Gets the method information of the class in the Web service and saves it by reflection, as follows:

Ensuremethod Method Analysis:

1 private void Ensuremethods ()
2 {
3 if (this._methods = null)
4 {
5 Lock (this)
6 {
7 dictionary<string, webservicemethoddata> methoddict =
8 new dictionary<string, webservicemethoddata> (stringcomparer.ordinalignorecase);
9
10//Get all public instance methods
One methodinfo[] Infoarray = This._type. GetMethods (BindingFlags.Public | BindingFlags.Instance);
12
13//Enumerate each MethodInfo
foreach (MethodInfo info in Infoarray)
15 {
16//Get WebMethodAttribute Callout
object[] Webmethodattarray = info. GetCustomAttributes (typeof (WebMethodAttribute), true);
18
19//If this method is WebMethodAttribute labeled
if (webmethodattarray.length!= 0)
21 {
22//Get Weboperationattribute Callout
object[] Webopattarray = info. GetCustomAttributes (typeof (Weboperationattribute), true);
24
25//Generate Webservicemethoddata Object
num webservicemethoddata data = new Webservicemethoddata (
This,
info,
(WebMethodAttribute) webmethodattarray[0],
(webopattarray.length!= 0)? ((Weboperationattribute) webopattarray[0]): null);
31
32//Into dictionary
Methoddict[info. Name] = data;
34}
35}
36
Panax Notoginseng this._methods = methoddict;
38}
39}
40}
Code to run here, you've got the method you want to execute. It is about to enter the ProcessRequest phase of the handler, where the input to the request is received and output is provided. So how does it work?

We will discuss the issue in the next 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.