科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件深入Atlas系列之服务器端支持

深入Atlas系列之服务器端支持

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

增加服务器端的支持其实就是添加/改变处理一个HTTP Request的方式。在ASP.NET中,是通过一个实现了System.Web.IHttpHandler接口的类来处理Request。

作者:老赵 来源:博客园 2007年11月3日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
可以发现,这就是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”。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章