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

首頁 > 開發 > 綜合 > 正文

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

2024-07-21 02:28:33
字體:
來源:轉載
供稿:網友

  在上一篇文章里,我們分析討論了使用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階段了,在那里會對接受這個請求的輸入,并提供輸出。那么它就是如何工作的呢?

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 寿光市| 湟源县| 乳山市| 遵义县| 巫山县| 乌什县| 黄梅县| 乳源| 临颍县| 白河县| 拜城县| 晴隆县| 彭州市| 图们市| 岱山县| 伊川县| 五莲县| 贵阳市| 高青县| 广德县| 武川县| 陆丰市| 普洱| 济阳县| 南开区| 安达市| 宾阳县| 天水市| 沿河| 治多县| 南充市| 徐闻县| 永定县| 桦南县| 海伦市| 南丹县| 阿拉善右旗| 穆棱市| 章丘市| 五河县| 定安县|