All articles| All Pictures| All Softwares| All Video| Go home page| Write articles| Upload pictures

Reading number is top 10 articles
DataBinder.Eval使用方法总结_[Asp.Net教程]
SQL数据库高级教程:学习 SQL AND & OR_[SQL Server教程]
简单示例:AJAX结合PHP代码实现登录_[PHP教程]
c#中GDI+图形图像:GDI+中的图形路径使用方法
ASP.NET,MVC+LINQ开发一个图书销售站点(6)-创建数据层_[Asp.Net教程]
C#实现Word中表格信息读取_[Asp.Net教程]
C#教程:拖放技术相关方法和事件使用实例
ASP.NET&Spring.NET&NHibernate最佳实践(二)——第1章前言_[Asp.Net教程]
PHP实例:PHP安全编程之加密功能_[PHP教程]
PHP动态网页编程:include()与require()的对比_php资料_编程技术
Reading number is top 10 pictures
The hot big eye big breast beauty1
刘亦菲写真集2
红楼梦金陵十二钗(1)
Hunan province aizhai super-large suspension bridge open to traffic and 4 world first1
联通的3G无线网卡我只用了一天,看看流量......
美丽的风景--让你目瞪口呆
The little girl with long hair2
The money of more than 100 countries and regions3
Fan bingbing black wings for platform and DanLuoWang believes beauty2
新版武松打虎
Download software ranking
网络管理员第三版
Boxer's Top ten classic battle10
Kung fu panda - the secret of the teacher
Boxer vs Yellow3
Unix video tutorial12
Unix video tutorial17
Tram sex maniac 2 (H) rar bag11
虚拟机5.5.3版
Prostitutes diary
SP3 for SQL2000
aaa published in(发表于) 2013/12/8 7:53:04 Edit(编辑)
深入Atlas系列之服务器端支持(上)_.net资料_编程技术

深入Atlas系列之服务器端支持(上)_.net资料_编程技术

深入Atlas系列之服务器端支持(上)_.net资料_编程技术-你的首页-uuhomepage.com

  在上一篇文章里,我们分析讨论了使用Atlas在进行AJAX访问Web Services所用的客户端代码。但是如果要实现这一功能,很显然还离不开服务器端的支持。在这篇文章里,我们就来讨论这一点。

  增加服务器端的支持其实就是添加/改变处理一个HTTP Request的方式。在ASP.NET中,是通过一个实现了System.Web.IHttpHandler接口的类来处理Request。我们可以在Web.config里通过配置将Request与实现IHttpHandler的类进行映射,以此告诉ASP.NET这个Request该由谁来处理。例如,在Atlas中,对于Culture的支持文件atlasglob.axd,就把该文件请求交由Microsoft.Web.Globalization.GlobalizationHandler类来处理。


<httpHandlers>
<add verb="*" path="atlasglob.axd" type="Microsoft.Web.Globalization.GlobalizationHandler" validate="false"/>
</httpHandlers>
  但是如果需要对于一个请求,使用不同的IHttpHandler来处理呢?甚者,如果需要对于已有一个请求的处理方式进行扩展呢?ASP.NET也考虑到了这一点,只需要将一个请求交给一个实现了System.Web.IHttpHandlerFactory接口的类即可。该类的功能就是根据该Request的一些“特点”,创建一个IHttpHandler实例。该类也提供了释放Hanlder的方法,提供了对于Handler实例复用的可能,减少由于构造和初始化对象的消耗,自然也减轻了GC的负担。

  在Atlas中就利用了这一点,改变了对于*.asmx请求的处理方式,对于在Query String中有mn的请求需要作特别的处理(在以后的文章中我会提到,对于“*.asmx/js”的请求,也会有另一种处理。它提供了客户端访问Web Services的代理,这超出了本篇文章的范围)。于是,如果需要使用Atlas从客户端以AJAX方式访问Web Services,则在Web.config里下面的设置绝对不可少:


<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" type="Microsoft.Web.Services.ScriptHandlerFactory" validate="false"/>
</httpHandlers>

  这个设置删除了原有*.asmx文件请求的映射,将*.asmx文件的请求交由Microsoft.Web.Services.ScriptHandlerFactory处理。这就是Atlas在服务器端的支持。

  接下来就要开始分析Atlas提供的Microsoft.Web.Atlas.dll里的代码了。这个程序集里的代码量和复杂程度均大大超过Atlas的客户端代码。因此,我只对于起关键作用的代码进行详细分析,一些辅助的方法或类的实现,只能请感兴趣的朋友们自行查看了。另外,为了大家阅读方便,我将局部变量名都改成了可读性比较高的名称,避免了“text1”,“flag1”之类的变量名,希望对大家阅读代码有所帮助。

  我们先来看一下Microsoft.Web.Services.ScriptHandlerFactory类的成员:

  ScriptHandlerFactory类成员:


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
10 private IHttpHandlerFactory _restHandlerFactory;
11 private IHttpHandlerFactory _webServiceHandlerFactory;
12
13 // Nested Types
14 private class AsyncHandlerWrapper : ScriptHandlerFactory.HandlerWrapper, IHttpAsyncHandler, IHttpHandler
15 {
16  // Methods
17   internal AsyncHandlerWrapper(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory);
18   public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
19   public void EndProcessRequest(IAsyncResult result);
20 }
21
22 private class AsyncHandlerWrapperWithSession : ScriptHandlerFactory.AsyncHandlerWrapper, IRequiresSessionState
23 {
24  // Methods
25  internal AsyncHandlerWrapperWithSession(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory);
26 }
27
28 internal class HandlerWrapper : IHttpHandler
29 {
30  // Methods
31  internal HandlerWrapper(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory);
32  public void ProcessRequest(HttpContext context);
33  internal void ReleaseHandler();
34
35  // Properties
36  public bool IsReusable { get; }
37
38  // Fields
39  private IHttpHandlerFactory _originalFactory;
40  protected IHttpHandler _originalHandler;
41 }
42
43 internal class HandlerWrapperWithSession : ScriptHandlerFactory.HandlerWrapper, IRequiresSessionState
44 {
45   // Methods
46   internal HandlerWrapperWithSession(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory);
47  }
48 }

  可以看到,除了IHttpHandlerFactory接口的方法外,类的内部还有着“丰富”地成员。CheckAtlasWebServicesEnabled()静态方法是查看是否提供Atlas访问WebServices的服务器端支持,如果不支持,则抛出异常。要让Atlas提供对于服务器端的支持,在Web.config里需要增加如下的元素:


<microsoft.web>
<webServices enableBrowserAccess="true" />
</microsoft.web>

  另外,在ScriptHandlerFactory类内部,有着数个内部类,它们提供了对于IHttpHandler对象的简单封装。在自己的代码中使用这样的Wrapper类,是扩展一个现有框架时常用的方法。通过阅读Microsoft.Web.Atlas.dll的代码,可以发现在Atlas中下至HttpRequest,上至Page,提供了大大小小十数个Wrapper类。

  我们从ScriptHandlerFactory的构造函数看起:

  ScriptHandlerFactory构造函数:


1 public ScriptHandlerFactory()
2 {
3  this._restHandlerFactory = new RestHandlerFactory();
4  this._webServiceHandlerFactory = new WebServiceHandlerFactory();
5 }

  构造函数相当简单,只是初始化了类的两个私有字段。ScriptHandlerFactory在工作时,会将产生和释放IHttpHander对象的责任,根据一定逻辑委托给这两个IHttpHandlerFactory类的对象之一。this._restHandlerFactory是Microsoft.Web.Services.RestHandlerFactory类的实例,负责处理Atlas对于*.asmx请求的扩展。而this._webServiceHandlerFactory是System.Web.Services.Protocols.WebServiceHandlerFactory类的实例,那么它又是什么呢?查看一个文件就能知晓,这个文件就是“%WINDOWS%\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.cofig”,它提供了ASP.NET全局的默认配置。我们可以在里面发现这样的设置:


<httpHandlers>
……
<add path="*.asmx" verb="*" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="False" />
……
</httpHandlers>


  可以发现,这就是ASP.NET原有处理*.asmx请求的类。Atlas的扩展要保证原有的功能不被破坏,因此使用了这个类对于扩展外的请求进行处理。   接下来进入IHttpHandlerFactory的关键方法:GetHandler。代码如下:

  GetHandler方法分析:


1 public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
2 {
3  IHttpHandlerFactory factory;
4
5  // 判断是否是Atlas扩展请求
6  if (RestHandlerFactory.IsRestRequest(context))
7  {
8   // 检测是否提供Atlas访问Web Services的支持
9   ScriptHandlerFactory.CheckAtlasWebServicesEnabled();
10  // 委托给RestHandlerFactory进行处理
11  factory = this._restHandlerFactory;
12 }
13 else
14 {
15  // 既然不是Atlas扩展请求,则使用ASP.NET原有的方式进行处理
16  factory = this._webServiceHandlerFactory;
17 }
18
19 // 调用Factory的GetHandler方法获得处理请求的Handler
20 IHttpHandler handler = factory.GetHandler(context, requestType, url, pathTranslated);
21
22 // 下面的代码就是根据Handler是否支持Session,
23 // 以及是否是异步Handler,选择不同的Wrapper类
24 // 进行封装并返回。
25 bool requiresSession = handler is IRequiresSessionState;
26 if (handler is IHttpAsyncHandler)
27 {
28  if (requiresSession)
29  {
30   return new ScriptHandlerFactory.AsyncHandlerWrapperWithSession(handler, factory);
31  }
32  return new ScriptHandlerFactory.AsyncHandlerWrapper(handler, factory);
33 }
34 if (requiresSession)
35 {
36  return new ScriptHandlerFactory.HandlerWrapperWithSession(handler, factory);
37 }
38 return new ScriptHandlerFactory.HandlerWrapper(handler, factory);
39 }

  四个Wrapper类为ScriptHandlerFactory.HandlerWrapper及其子类。之所以分如此多的封装类,是为了在进行统一封装的同时,保留ASP.NET的原有功能不变。有了统一的封装,ScriptHandlerFactory得ReleaseHandler也能非常轻易的处理,只需将责任委托给Handler本身,被分装的Handler本身能够知道自己应该用什么Factory来释放自己。代码如下:

  ReleaseHandler代码:


1 public virtual void ReleaseHandler(IHttpHandler handler)
2 {
3  if (handler == null)
4  {
5   throw new ArgumentNullException("handler");
6  }
7  ((ScriptHandlerFactory.HandlerWrapper) handler).ReleaseHandler();
8 }

  接下来要关心的就是RestHandlerFactory类的GetHandler方法了,代码如下:

  RestHanderFactory的GetHandler方法分析:


1 public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
2 {
3  // 如果是请求“*.asmx/js”,则说明是要求Web Services代理
4  if (RestHandlerFactory.IsClientProxyRequest(context.Request.PathInfo))
5  {
6   // 那么返回处理代理的Handler
7   return new RestClientProxyHandler();
8  }
9
10  // 使用静态函数CreateHandler得到Handler。
11  return RestHandler.CreateHandler(context);
12 }

  对于Atlas请求Web Services代理的请求将在以后的文章中进行讨论,现在我们只关心RestHandler的静态方法CreateHandler(HttpContext)的行为。代码如下:

  CreateHandler(HttpContext)方法分析:


1 internal static IHttpHandler CreateHandler(HttpContext context)
2 {
3  // 使用WebServiceData的静态方法GetWebServiceData(string)获得WebServiceData对象,
4  // 它描述了即将使用的那个Web Services的信息。
5  WebServiceData data = WebServiceData.GetWebServiceData(context.Request.Path);
6  // 获得Method Name
7  string methodName = context.Request.QueryString["mn"];
8  // 使用CreateHandler(WebServiceData, string)获得Handler
9  return RestHandler.CreateHandler(data, methodName);
10 }

  这里出现了一个非常重要的类,那就是WebServiceData,它封装了通过Atlas访问的Web Services,并提供了缓存等重要功能。这个类可以说是Atlas访问Web Serivces的重要组成部分,接下来我将对它进行简单的分析。实事求是地说,了解这些代码(乃至对整个服务器端代码的分析)并不会对Atlas技术的使用能力产生直接的效果,因此不感兴趣的朋友可以跳过这部分,而直接看之后的结论与范例。:)

  在分析WebServiceData.GetWebServiceData(string)之前,我们先看一下这个类的静态构造函数。

  WebServiceData静态构造函数分析:


1 static WebServiceData()
2 {
3  // Cache:以Web Services的Type作为key,WebServiceData的实例作为value
4  WebServiceData._cache = Hashtable.Synchronized(new Hashtable());
5  // Cache:以*.asmx文件的Virtual Path作为key,Web Service的Type作为value
6  WebServiceData._mappings = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
7  // Cache:和上面正好相反,以Type作为key,Virtual Path作为value
8  WebServiceData._typeVirtualPath = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
9 }

  静态构造函数的作用是初始化每个Cache对象,Atlas使用了Synchronized Hashtable作为Cache的容器。似乎.NET Framework 2.0没有提供Synchronized Generic Collection,颇为遗憾。

  接下来要看的就是静态方法GetWebServiceData(string)了,不过它只是直接使用了静态方法GetWebServiceData(string, bool),并将第二个参数设为true,那么我们就跳过它,直接看静态方法GetWebServiceData(string bool)的实现。代码如下:

  GetWebServiceData方法分析:


1 internal static WebServiceData GetWebServiceData(string virtualPath, bool failIfNoData)
2 {
3  if (virtualPath.EndsWith("bridge.axd", StringComparison.InvariantCultureIgnoreCase))
4  {
5   virtualPath = virtualPath.Substring(0, virtualPath.Length - 10) + ".asbx";
6  }
7
8  // 得到绝对路径(~/xxxxx/xxx.xxx)
9  virtualPath = VirtualPathUtility.ToAbsolute(virtualPath);
10 // 设法从Cache内获得Web Service的Type
11 Type wsType = WebServiceData._mappings[virtualPath] as Type;
12
13 bool wsFileExists = false;
14 // 如果Cache内没有
15 if (wsType == null)
16 {
17  // 查看访问的Web Service文件是否存在
18  wsFileExists = HostingEnvironment.VirtualPathProvider.FileExists(virtualPath);
19  // 如果存在的话
20  if (wsFileExists)
21  {
22   // 将Web Service文件编译并得到其Type
23   wsType = BuildManager.GetCompiledType(virtualPath);
24  }
25 }
26
27 // 如果没有得到Type,并且Web Services文件不存在,
28 // 说明这不是用户提供的Web Service,而是使用程序集
29 // 自己提供的类型,于是就在程序集里进行寻找。
30 if ((wsType == null) && !wsFileExists)
31 {
32  string typeName = null;
33  int num1 = virtualPath.IndexOf("ScriptServices/");
34  // 如果路径里有ScriptServices/
35  if (num1 != -1)
36  {
37    num1 += "ScriptServices/".Length;
38    // 截取"ScriptServices/"后面的字符串,并且将扩展名去掉
39    typeName = virtualPath.Substring(num1, (virtualPath.Length - num1) - 5);
40    // 将所有的'/'换成'.',这样就变成了一个类的FullName。
41    typeName = typeName.Replace('/', '.');
42    // 从Atlas自身的程序集得到这个类型。
43    wsType = typeof(WebServiceData).Assembly.GetType(typeName, false, true);
44    // 如果Atlas程序集里没有这个类型,那么在全局找这个类型
45    if (wsType == null)
46    {
47     wsType = BuildManager.GetType(typeName, false, true);
48    }
49  }
50  else
51  {
52   try
53   {
54    // 去掉扩展名
55    typeName = Path.GetFileNameWithoutExtension(virtualPath);
56    // 使用Reflection调用Sys.Web.UI.Page的DecryptString获得typeName
57    typeName = WebServiceData.DecryptString(typeName);
58    wsType = Type.GetType(typeName);
59   }
60   catch
61   {
62   }
63
64   if (wsType != null)
65   {
66    // 在Cache保存Type对象和Virtual Path之间的对应关系。
67    WebServiceData._mappings[virtualPath] = wsType;
68    WebServiceData._typeVirtualPath[wsType] = virtualPath;
69   }
70  }
71 }
72
73 // 如果得到了Web Service的Type
74 if (wsType != null)
75 {
76  // 通过静态方法GetWebServiceData(Type)得到WebServiceData对象
77  return WebServiceData.GetWebServiceData(wsType);
78 }
79
80 if (failIfNoData)
81 {
82  throw new InvalidOperationException();
83 }
84
85 return null;
86 }

  方法内部使用了部分.NET Framework 2.0提供的方法,如果希望具体了解这些方法请参考MSDN。

  这是个比较复杂的方法,不过对它的阅读能够让使用Atlas的方式上一个新的台阶。上面的代码经过了注释,应该已经可以比较方便的理解了。在这里可能需要我详细解释一下第35到第49行的具体含义。这段逻辑目的是将一个路径映射一个类型,目前在Atlas中的应用就是在使用Authentication Service的时候,它事实上是请求了一个路径“ScriptServices/Microsoft/Web/Services/Standard/AuthenticationWebService.asmx”,自然在客户端不会有这个文件。于是就会将这个路径去除扩展名和ScriptServices等字样,变成了“Microsoft/Web/Services/Standard/AuthenticationWebService”,再将所有的“/”变成“.”,就成为了一个类的标识“Microsoft.Web.Services.Standard.AuthenticationWebService”,您可以在程序集中找到这个类。同样的作法,也存在于Atlas的Profile Service中,它请求的Web Services是“ScriptServices/Microsoft/Web/Services/Standard/ProfileWebService.asmx”。


  对于开发人员来说,它的价值就是:我们能够把一个Web Service方法的请求处理编译在程序集之中!例如,我们只需要写一个Jeffz.Atlas.SampleService类继承System.Web.Services.WebService,并在javascript中请求“ScriptServices/Jeffz/Atlas/SampleService.asmx”即可。这对于发布和部署组建提供了非常大的便利,对于喜欢编写Extender的朋友们,也提供了和服务器端交互的完美方式。关于这一点,我会在这一系列接下去的文章中给与具体的范例供大家参考。

  继续回到对代码的分析,GetWebServiceData(string, bool)最终是返回了GetWebServiceData(Type)调用结果。代码如下:

  GetWebServiceData(Type)方法分析:


1 private static WebServiceData GetWebServiceData(Type type)
2 {
3 // 设法从Cache内获得WebServiceData对象
4 WebServiceData data = WebServiceData._cache[type] as WebServiceData;
5
6 // 如果Cache内没有
7 if (data == null)
8 {
9 // 构造该对象
10 data = new WebServiceData(type);
11 // 并放入Cache中
12 WebServiceData._cache[type] = data;
13 }
14
15 return data;
16 }

  代码非常简单,就不多作解释了。WebServiceData类的构造函数也无需分析,只是简单的保留那个Type而已。代码如下:

  WebServiceData构造函数:


1 private WebServiceData(Type type)
2 {
3 this._type = type;
4 }

  WebServiceData类的分析到这里先告一段落,我们回到之前的代码。获得IHttpHandler对象是调用了RestHandler的CreateHandler(WebServiceData, string)静态方法,代码如下:

  CreateHandler(WebServiceData, string)静态方法分析:


1 private static IHttpHandler CreateHandler(WebServiceData webServiceData, string methodName)
2 {
3 RestHandler handler;
4 // 调用GetMethodData得到WebServiceMethodData对象实例,
5 // 描述了一个Web Service方法。
6 WebServiceMethodData data = webServiceData.GetMethodData(methodName);
7
8 // 根据是否支持Session选择不同的Handler
9 if (data.RequiresSession)
10 {
11 handler = new RestHandlerWithSession();
12 }
13 else
14 {
15 handler = new RestHandler();
16 }
17
18 handler._webServiceMethodData = data;
19 return handler;
20 }

  这里出现了对于Web Services方法的描述类WebServiceMethodData,通过WebServiceData的GetMethodData方法获得。该方法代码如下:

  GetMethodData方法分析:


1 internal WebServiceMethodData GetMethodData(string methodName)
2 {
3 // 保证Method的描述都被加载并保存了
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
12 return data;
13 }

  this.EnsureMethod()方法通过反射得到了Web Service中类的方法信息并保存下来,代码如下:

  EnsureMethod方法分析:


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 // 获得所有public的实例方法
11 MethodInfo[] infoArray = this._type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
12
13 // 枚举每个MethodInfo
14 foreach (MethodInfo info in infoArray)
15 {
16 // 获得WebMethodAttribute标注
17 object[] webMethodAttArray = info.GetCustomAttributes(typeof(WebMethodAttribute), true);
18
19 // 如果这个方法被WebMethodAttribute标注了
20 if (webMethodAttArray.Length != 0)
21 {
22 // 获得WebOperationAttribute标注
23 object[] webOpAttArray = info.GetCustomAttributes(typeof(WebOperationAttribute), true);
24
25 // 生成WebServiceMethodData对象
26 WebServiceMethodData data = new WebServiceMethodData(
27 this,
28 info,
29 (WebMethodAttribute)webMethodAttArray[0],
30 (webOpAttArray.Length != 0) ? ((WebOperationAttribute)webOpAttArray[0]) : null);
31
32 // 放入Dictionary
33 methodDict[info.Name] = data;
34 }
35 }
36
37 this._methods = methodDict;
38 }
39 }
40 }

  代码运行到此处,已经获得要执行的那个方法。马上就要进入了Handler的ProcessRequest阶段了,在那里会对接受这个请求的输入,并提供输出。那么它就是如何工作的呢?

  我们将在下一篇文章中讨论这个问题。






添加到del.icio.us 添加到新浪ViVi 添加到百度搜藏 添加到POCO网摘 添加到天天网摘365Key 添加到和讯网摘 添加到天极网摘 添加到黑米书签 添加到QQ书签 添加到雅虎收藏 添加到奇客发现 diigo it 添加到饭否 添加到飞豆订阅 添加到抓虾收藏 添加到鲜果订阅 digg it 貼到funP 添加到有道阅读 Live Favorites 添加到Newsvine 打印本页 用Email发送本页 在Facebook上分享


Disclaimer Privacy Policy About us Site Map

If you have any requirements, please contact webmaster。(如果有什么要求,请联系站长)
Copyright ©2011-
uuhomepage.com, Inc. All rights reserved.