国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

深入Atlas系列之服務器端支持(上)

2019-11-17 04:51:56
字體:
來源:轉載
供稿:網友

  在上一篇文章里,我們分析討論了使用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.Scr</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”。
更多的請看:http://www.QQread.com/windows/2003/index.Html
  對于開發人員來說,它的價值就是:我們能夠把一個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階段了,在那里會對接受這個請求的輸入,并提供輸出。那么它就是如何工作的呢?

  我們將在下一篇文章中討論這個問題。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 青河县| 西畴县| 衡阳县| 綦江县| 文山县| 东兰县| 湖北省| 古交市| 永州市| 嘉祥县| 枣阳市| 石柱| 枣强县| 潜山县| 河南省| 南平市| 大英县| 平远县| 南溪县| 巨鹿县| 广德县| 新和县| 鄱阳县| 黑河市| 贡嘎县| 新平| 萍乡市| 永兴县| 博白县| 贵阳市| 宾川县| 山西省| 绥化市| 康平县| 凉山| 山阴县| 洪江市| 罗田县| 策勒县| 平遥县| 泾川县|