91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用ASP.NET寫服務框架

發布時間:2021-12-06 15:26:17 來源:億速云 閱讀:149 作者:iii 欄目:編程語言

這篇文章主要介紹“怎么用ASP.NET寫服務框架”,在日常操作中,相信很多人在怎么用ASP.NET寫服務框架問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么用ASP.NET寫服務框架”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

首先我要談的話題是ASP.NET的請求處理【管線】,我認為這是ASP.NET中最重要的內容了,所有到達ASP.NET的請求都要經過管線來處理,不管是WebForms, MVC, WebService, WCF(ASP.NET的承載方式),還是其它微軟的采用HTTP協議的框架。為什么這些框架都選擇要ASP.NET做為它們的運行平臺呢?

我們可以考慮一下:如果讓您從無到有設計一個服務框架,有哪些事件是必須要處理的?

我想有三個最根本的事件要做:1. 監聽請求端口,2. 為每個傳入的連接請求分配線程來執行具體的響應操作,3. 要把請求的數據讀出來,并負責將處理后的響應數據發送給調用者。

這其實是個比較復雜也很枯燥的過程,但每個服務器端程序都需要這些基本功能。幸好IIS和ASP.NET可以為我們做好這些事情,所以那些框架選擇ASP.NET平臺就可以省去這些復雜的任務。使用ASP.NET平臺不僅可以簡化設計,它還有著良好的擴展性以滿足更多的框架在這個平臺上面繼續開發,而這個良好擴展性是離不開它的請求處理管線的。

ASP.NET是一個功能完善的平臺框架,它既提供一些高層次的框架供我們使用,比如:WebForms, MVC, WebService,也提供一些低層次的機制讓我們使用,以便于讓我們開發有特殊要求的新框架,新解決方案。這個低層次的機制就是請求處理管線,使用這個管線的有二類對象:HttpHandler, HttpModule,控制這條管線工作的對象是:HttpApplication 。通常情況下,我們并不需要直接使用HttpApplication對象,因此本文的主題將主要介紹HttpHandler, HttpModule這二類對象的功能以及如何使用它們。

理解ASP.NET管線

管線(Pipeline)這個詞也是很有點意思,這個詞也形象地說明了每個ASP.NET請求的處理過程:請求是在一個管道中,要經過一系列的過程點,這些過程點連接起來也就形成一條線。以上是我對于這個詞的理解,如果有誤,懇請給予指正。這些一系列的過程點,其實就是由HttpApplication引發的一系列事件,通常可以由HttpModule來訂閱,也可以在Global.asax中訂閱,這一系列的事件也就構成了一次請求的生命周期。

事件模式,也就是觀察者模式。根據【C# 3.0 設計模式】一書中的定義:“觀察者模式定義了對象之間的一種聯系,使得當一個對象改變狀態時,所有其它的對象都可以相應地被通知到。" ASP.NET的管線設計正是采用了這種方式,在這個設計模式中,觀察者就是許多HttpModule對象,被觀察的對象就是每個”請求“,它的狀態是由HttpApplication控制,用于描述當前請求的處理階段,HttpApplication會根據一個特定的順序修改這個狀態,并在每個狀態改變后引發相應的事件。 ASP.NET會為每個請求分配一個HttpApplication對象來引發這些事件,因此可以讓一大批觀察者了解每個請求的狀態,每個觀察者也可以在感興趣的時候修改請求的一些數據。這些與請求相關的數據的也就是我上篇博客中提到的HttpRequest, HttpResponse。正是由于引入了事件機制,ASP.NET框架也有了極強的擴展能力。再來看看管線處理請求的過程,我將直接引用MSDN中的原文【IIS 5.0 和 6.0 的 ASP.NET 應用程序生命周期概述】中的片段。

在處理該請求時將由 HttpApplication 類執行以下事件。 希望擴展 HttpApplication 類的開發人員尤其需要注意這些事件。

1. 對請求進行驗證,將檢查瀏覽器發送的信息,并確定其是否包含潛在惡意標記。 有關更多信息,請參見 ValidateRequest 和腳本侵入概述。

2. 如果已在 Web.config 文件的 UrlMappingsSection 節中配置了任何 URL,則執行 URL 映射。

3. 引發 BeginRequest 事件。

4. 引發 AuthenticateRequest 事件。

5. 引發 PostAuthenticateRequest 事件。

6. 引發 AuthorizeRequest 事件。

7. 引發 PostAuthorizeRequest 事件。

8. 引發 ResolveRequestCache 事件。

9. 引發 PostResolveRequestCache 事件。

10. 根據所請求資源的文件擴展名(在應用程序的配置文件中映射),選擇實現 IHttpHandler 的類,對請求進行處理。 如果該請求針對從 Page 類派生的對象(頁),并且需要對該頁進行編譯,則 ASP.NET 會在創建該頁的實例之前對其進行編譯。

11. 引發 PostMapRequestHandler 事件。

12. 引發 AcquireRequestState 事件。

13. 引發 PostAcquireRequestState 事件。

14. 引發 PreRequestHandlerExecute 事件。

15. 為該請求調用合適的 IHttpHandler 類的 ProcessRequest 方法(或異步版 IHttpAsyncHandler.BeginProcessRequest)。 例如,如果該請求針對某頁,則當前的頁實例將處理該請求。

16. 引發 PostRequestHandlerExecute 事件。

17. 引發 ReleaseRequestState 事件。

18. 引發 PostReleaseRequestState 事件。

19. 如果定義了 Filter 屬性,則執行響應篩選。

20. 引發 UpdateRequestCache 事件。

21. 引發 PostUpdateRequestCache 事件。

22. 引發 EndRequest 事件。

23. 引發 PreSendRequestHeaders 事件。

24. 引發 PreSendRequestContent 事件。

如果是IIS7,第10個事件也就是MapRequestHandler事件,而且在EndRequest 事件前,還增加了另二個事件:LogRequest 和 PostLogRequest 事件。

只有當應用程序在 IIS 7.0 集成模式下運行,并且與 .NET Framework 3.0 或更高版本一起運行時,才會支持 MapRequestHandler、LogRequest 和 PostLogRequest 事件。

這里要補充一下:從BeginRequest開始的事件,并不是每個事件都會被觸發,因為在整個處理過程中,隨時可以調用Response.End()或者有未處理的異常發生而提前結束整個過程。在那些"知名"的事件中,也只有EndRequest事件是肯定會觸發的,(部分Module的)BeginRequest有可能也不會被觸發。

對于這些管線事件,我只想提醒2個非常重要的地方:

1. 每個請求都將會映射到一個HttpHandler,通常也是處理請求的主要對象。

2. HttpModule可以任意訂閱這些事件,在事件處理器中也可以參與修改請求的操作。

這2點也決定了HttpHandler和HttpModule的工作方式。

我找了二張【老圖片】,希望能更直觀的說明ASP.NET管線的處理過程。結合我前面講述的內容,再品味一下老圖片吧。

怎么用ASP.NET寫服務框架

怎么用ASP.NET寫服務框架

HttpHandler

HttpHandler通常是處理請求的核心對象。絕大多數的的請求都在【第10步】被映射到一個HttpHandler,然后在【第15步】中執行處理過程,因此也常把這類對象稱為處理器或者處理程序。我們熟知的Page就是一個處理器,一個ashx文件也是一個處理器,不過ashx顯示得更原始,我們還是來看一下ashx通常是個什么樣子:

<%@ WebHandler Language="C#" Class="Login" %> using System;  using System.Web;  public class Login : IHttpHandler {  public void ProcessRequest (HttpContext context) {  context.Response.ContentType = "text/plain";  string username = context.Request.Form["name"];  string password = context.Request.Form["password"];   if( password == "aaaa" ) {  System.Web.Security.FormsAuthentication.SetAuthCookie(username, false);  context.Response.Write("OK");  }  else {  context.Response.Write("用戶名或密碼不正確。");  }  }  public bool IsReusable {  get {  return false;  }  }  }

可以看到它僅僅是實現一個IHttpHandler接口而已,IHttpHandler接口也很簡單:

// 定義 ASP.NET 為使用自定義 HTTP 處理程序同步處理 HTTP Web 請求而實現的協定。  public interface IHttpHandler  {  // 獲取一個值,該值指示其他請求是否可以使用 System.Web.IHttpHandler 實例。  //  // 返回結果:  // 如果 System.Web.IHttpHandler 實例可再次使用,則為 true;否則為 false。  bool IsReusable { get;}  // 通過實現 System.Web.IHttpHandler 接口的自定義 HttpHandler 啟用 HTTP Web 請求的處理。  void ProcessRequest(HttpContext context);  }

IsReusable屬性上面有注釋,我就不說了。接口中最重要的部分就是方法 void ProcessRequest(HttpContext context);這個方法簡單地不能再簡單,只有一個參數,但這個參數的能量可不小,有了它幾乎就有了一切,這就是我對它的評價。關于HttpContext的更多詳細介紹請參考我的博客【我心目中的ASP.NET核心對象】。

在Login.ashx中,我做了三簡單的事:

1. 讀取輸入數據: 從Request.Form中。

2. 執行特定的業務邏輯: 一個簡單的判斷。

3. 返回結果給客戶端: 調用Response.Write()

是的,就是這三個簡單的操作,但也是絕大多數ashx文件的常規寫法,它的確可以完成一次請求的處理過程。

記住:事實上任何HttpHandler都是這樣處理請求的,只是有時會借助一些框架的包裝而變了味道而已。

我認為:HttpHandler的強大離不開HttpContext,HttpHandler的重要性是因為管線會將每個請求都映射到一個HttpHandler。

通常,我們需要新的HttpHandler,創建一個ashx文件就可以了。但也可以創建自己的HttpHandler,或者要將一類【特殊的路徑/擴展名】交給某個處理器來處理,那么就需要我們在web.config中注冊那個處理器。

注意:如果是【特殊的擴展名】可能還需要在IIS中注冊,原因很簡單:IIS不將請求交給ASP.NET,我們的代碼根本沒機會運行!

我們可以采用以下方式在web.config中注冊一個自定義的處理器:

<httpHandlers>  <add path="/MyService.axd" verb="*" validate="false" type="MySimpleServiceFramework.MyServiceHandler"/>  </httpHandlers>

或者:(為了排版,我將一些代碼做了換行處理)

<httpHandlers>  <remove verb="*" path="*.cs"/>  <add verb="*" path="*.cs" validate="false" type="FishWebLib.Ajax.AjaxMethodV2Handler,  FishWebLib, Version=3.0.0.0, Culture=neutral, PublicKeyToken=04db02423b9ebbb2"/>FishWebLib, Version=3.0.0.0, Culture=neutral, PublicKeyToken=04db02423b9ebbb2"/>  <remove verb="*" path="*.ascx"/>  <add verb="*" path="*.ascx" validate="false" type="FishWebLib.Ajax.UserControlHandler,  FishWebLib, Version=3.0.0.0, Culture=neutral, PublicKeyToken=04db02423b9ebbb2"/>FishWebLib, Version=3.0.0.0, Culture=neutral, PublicKeyToken=04db02423b9ebbb2"/>  </httpHandlers>

HttpModule

前面我已經提到過HttpModule的工作方式:訂閱管線事件,并在事件處理器中執行所需的相關操作。

這個描述看起來很平淡,但是,它的工作方式給了它無限強大的處理能力。

它的無限強大的處理能力來源于可以訂閱管線事件,因此,它有能力可以在許多階段修改請求,這些修改最終可能會影響請求的處理。

前面我說過:“HttpHandler是處理請求的主要對象”,但HttpModule卻可以隨意指定將某個請求交給某個處理器來執行!

甚至,HttpModule也可以直接處理請求,完全不給HttpHandler工作的機會!

我們來看一下HttpModule的實現方式:

/// <summary>  /// 能支持雙向GZIP壓縮的Module,它會根據客戶端是否啟用GZIP來自動處理。  /// 對于服務來說,不用關心GZIP處理,服務只要處理輸入輸出就可以了。  /// </summary>  internal class DuplexGzipModule : IHttpModule  {  public void Init(HttpApplication app)  {  app.BeginRequest += new EventHandler(app_BeginRequest);  }  void app_BeginRequest(object sender, EventArgs e)  {  HttpApplication app = (HttpApplication)sender;  // 注意:這里不能使用"Accept-Encoding"這個頭,二者的意義完全不同。  if( app.Request.Headers["Content-Encoding"] == "gzip" ) {  app.Request.Filter = new GZipStream(app.Request.Filter, CompressionMode.Decompress);  app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);  app.Response.AppendHeader("Content-Encoding", "gzip");  }  }  public void Dispose()  {  }  }

每個HttpModule只需要實現IHttpModule接口就可以了。IHttpModule也是個簡單的接口:

public interface IHttpModule  {  void Dispose();  void Init(HttpApplication app);  }

在這二個方法中,***個方法通常可以保持為空。最重要的方法就是Init,它給了HttpModule能訂閱管線事件的機會,然后在相應的事件處理中,我們就可以執行它的具體操作了。

還記得我在博客【我心目中的ASP.NET核心對象】***給出一個示例嗎?在QueryOrderService.ashx中,為了支持gzip,需要直接調用GZipStream類,對于一二個ashx來說,或許不是問題,如果這樣的處理器變多了,每個處理器都那樣寫,您能受得了嗎?反正我是受不了的,因此今天我把它改成使用Module來實現,代碼簡單了許多。在本文末尾可以下載。

對于ASP.NET項目來說:當您發現有很多處理輸入輸出的操作非常類似時,那正是HttpModule可以發揮的舞臺,請把這些重復的操作交給它吧。

讓HttpModule工作也需要在web.config中注冊:

<httpModules>  <add name="DuplexGzipModule" type="MySimpleServiceFramework.DuplexGzipModule"/>  </httpModules>

通常,我會把一些HttpModule放在類庫中實現,然后在需要使用的項目的web.config中注冊。

這也體現它的高重用性:寫一次,許多項目就可以直接使用。

HttpModule的加載方式:前面我說過“ASP.NET會為每個請求分配一個HttpApplication對象”,在每個HttpApplication對象的初始化操作中,它會加載所有在web.config中注冊的HttpModule。由于ASP.NET并不是只創建一個HttpApplication對象,而是多個HttpApplication對象,因此每個HttpModule的Init事件是有可能被多次調用的。許多人喜歡在這里做各類初始化的操作,那么請注意在這里修改靜態變量成員時的線程安全問題。特別地,如果要執行程序初始化的操作,那么還是把代碼放在Global.asax的Application_Start中去處理吧, HttpModule的Init事件并不合適。

為HttpModule選擇訂閱合適的管線事件:這是非常重要的,訂閱不同的事件,產生的結果也會不一樣。原因也很簡單,在ASP.NET運行環境中,并不只有一個HttpModule,某個HttpModule的判斷可能要依據其它HttpModule的輸出結果,而且在某些(晚期的)管線事件中,也不能再修改輸出數據。在后面的示例中,DirectProcessRequestMoudle訂閱了PostAuthorizeRequest事件,如果訂閱BeginRequest事件或許將得到更好的性能,但是,在BeginRequest事件中,身份認證模塊還沒有工作,因此每個請求在這個事件階段都屬于“未登錄”狀態。

前面說了一大堆的HttpModule,事實上,在這個示例中,主角是另一個對象:Filter 。上篇博客我就提過它,***為了演示它,把它放在一個HttpHandler里【糟蹋了】,沒辦法,上篇的主題不是管線呀。今天只好和HttpModule一起出場了。我認為Filter還是應該和HttpModule一起使用才能發揮它的獨特價值。Filter的特點還真不合適在HttpHandler中使用,如果您在HttpHandler里使用Filter,我認為有必要考慮一下是不是用錯了。

借HttpModule的地盤我們來談談Filter。Filter很低調,低調到什么程度:可能很少有人關注過它,因此也少有人用過它。事實也確實如此,一般情況下可以不用它,但用到它,你會發現它非常強大。前面我經常說到【輸入輸出流】,請求的數據,除了請求頭以外,基本上全放在流中,如果您希望對這些數據以流的方式進行處理,特別是希望對于所有請求,或者某類請求,那么使用Filter是非常恰當的。前面的示例就是一個非常合理地使用,好好地品味它,或許您還能發現Filter能做更多的事情。

選 HttpHandler 還是 HttpModule ?

HttpHandler是每個請求的主要處理對象,而HttpModule可以選擇請求交給哪個HttpHandler來處理,甚至,它還可以選擇它自己來處理請求。

下面我給個示例代碼來說明HttpModule也能直接請求:

/// <summary>  /// 此Module示范了直接使用Module也能處理客戶端的請求。  /// 建議:除非要很好的理由,否則不建議使用這種方法。  /// </summary>  internal class DirectProcessRequestMoudle : IHttpModule  {  public void Init(HttpApplication app)  {  app.PostAuthorizeRequest += new EventHandler(app_PostAuthorizeRequest);  }  void app_PostAuthorizeRequest(object sender, EventArgs e)  {  HttpApplication app = (HttpApplication)sender;   ServiceInfo info = GetServiceInfo(app.Context);  if( info == null )  return;  ServiceExecutor.ProcessRequest(app.Context, info);  app.Response.End();  }

為了更好的回答本節的這個問題,我再給段等效的代碼,不過,請求是經過HttpHandler來處理

 internal class MyServiceHandler : IHttpHandler  {   internal ServiceInfo ServiceInfo { get;set;}  public void ProcessRequest(HttpContext context)  {  ServiceInfo info = this.ServiceInfo ?? GetServiceInfo(context);  ServiceExecutor.ProcessRequest(context, info);  }

HttpHandler和HttpModule都能處理請求,我該選哪個??

對于此類情況,我的答案是:視情況而定,正如我在注釋中描述的那樣,除非要很好的理由,否則不建議使用HttpModule處理請求。用HttpModule在某些時候可能會快點,關鍵點在于處理完成時要調用Response.End();這會讓后面的事件全都短路,其它的HttpModule就沒有機會執行。如果您的框架或者項目設計很依賴于管線中的事件處理,那么調用Response.End();無疑會破壞這個規則,也將會導致不能得到正確的結果。選擇HttpHandler就不會有這種事情發生。

不過,也沒有絕對的事情:在請求處理期間,您可以在任何地方調用Response.End(); 結果也是一樣的。

幸好,短路的情況并不經常發生,因此選擇HttpHandler會讓整個ASP.NET的管線都能發揮作用,因此,我建議優先選擇HttpHandler。

尤其是在HttpHandler能很好的完成工作的前提下,就應該選HttpHandler,因為選HttpModule會給其它請求帶來不必要的性能損失,具體細節請繼續閱讀。

其實,我們還可以從另一個角度來看這個問題。

首先,請仔細地閱讀前面的示例代碼,您是否發現它們在實現方式上非常類似?

現在應該找到答案了吧:把具體的處理操作分離到HttpHandler,HttpModule之外的地方。那么,此時這個問題也就不是問題了,您也可以提供多種方案供使用者選擇。比如:我就為【我的服務框架】提供了5種方式讓使用者可以輕松地將一個C#方法公開為一個服務方法,該如何選擇這個問題,由使用者來決定,這個問題不會讓我為難。

我的觀點:在沒有太多技術難度的前提下,提供多種解決辦法應該是對的,您將會避開很多麻煩事情。

看不見的性能問題

前面我介紹了HttpModule的重要優點:高重用性。只要寫好一個HttpModule可以放在任何ASP.NET項目中使用,非常方便。

不過,再好的東西也不能濫用。HttpModule也可以對性能產生負面影響。原因也很簡單:對于每個ASP.NET請求,每個HttpModule都會在它們所訂閱的事件中,去執行一些操作邏輯。這些操作邏輯或許對一些請求是無意義的,但仍會執行。因此,計算機將會白白浪費一些資源去執行一些無意義的代碼。

知道了原因,解決辦法也就很清楚了:

1. 去掉不需要的HttpModule

2. 在每個HttpModule的事件處理器中,首先要確定是不是自己所需要處理的請求。對一些不相關的請求,應該立即退出。

在我們創建一個ASP.NET項目時,如果不做任何修改,微軟已經為我們加載了好多HttpModule 。請看以下代碼:

protected void Page_Load(object sender, EventArgs e)  {  HttpApplication app = HttpContext.Current.ApplicationInstance;  StringBuilder sb = new StringBuilder();  foreach( string module in app.Modules.AllKeys )  sb.AppendFormat(module).Append("<br />");  Response.Write(sb.ToString());   }

輸出結果如下:

怎么用ASP.NET寫服務框架

總共有14個。

哎,大多數是我肯定不會用到的,但它們卻被加載了,因此,在它們所訂閱的事件中,它們的代碼將會檢查所有的請求,那些無意義的代碼將有機會執行。如果您不想視而不見,那么請在web.config中做類似的修改,將不需要的Module移除。

<httpModules> <remove name="Session"/> <remove name="RoleManager"/> </httpModules>

HttpModule的第2個需要注意的地方是:HttpModule對所有的請求有效,如果HttpModule不能處理所有的請求,那么請先判斷當前請求是否需要處理,對于不需要處理的請求,應該立即退出。請看以下示例代碼:

/// <summary>  /// 【演示用】讓Aspx頁的請求支持gzip壓縮輸出  /// </summary>  public class FishGzipModule : IHttpModule  {  public void Init(HttpApplication app)  {  app.BeginRequest += new EventHandler(app_BeginRequest);   }  void app_BeginRequest(object sender, EventArgs e)  {  HttpApplication app = (HttpApplication)sender;  // 這里做個簡單的演示,只處理aspx頁面的輸出壓縮。  // 當然了,IIS也提供壓縮功能,這里也僅當演示用,或許可適用于一些特殊場合。  if( app.Request.AppRelativeCurrentExecutionFilePath.EndsWith(  "aspx", StringComparison.OrdinalIgnoreCase) == false )  // 注意:先判斷是不是要處理的請求,如果不是,直接退出。  // 而不是:先執行了后面的判斷,再發現不是aspx時才退出。  return;  string flag = app.Request.Headers["Accept-Encoding"];  if( string.IsNullOrEmpty(flag) == false &&flag.ToLower().IndexOf("gzip") >= 0 ) {  app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);  app.Response.AppendHeader("Content-Encoding", "gzip");  }  }

更多實戰介紹

本文從這里起,將不再過多的敘述一些理論文字,而是將以實戰的形式展示ASP.NET的強大管線功能,這些實戰展示了一些很經典的應用場景,其中大部分示例代碼將做為【我的服務框架】的關鍵部分。因此請注意理解這些代碼。

實戰代碼大量使用了上篇博客【我心目中的ASP.NET核心對象】所介紹的絕大多數對象,也算是再次展示那些核心對象的重要性,因此請務必先了解那些核心對象。

上篇博客僅展示了那些強大對象的功能,單獨使用它們,也是不現實的,今天,我將演示它們與HttpHandler, HttpModule一起并肩工作所能完成的各種任務。

故事未講完,傳奇在繼續。更多精彩即將上演!

實戰演示 - 模擬更多的HttpMethod

近幾年又有一種被稱為RESTful Web服務的概念進入開發人員的視野,它提倡使用HTTP協議提供的GET、POST、PUT和DELETE方法來操作網絡資源。不過,目前的瀏覽器只支持GET、POST這二種方法,因此就有人想到采用HTTP頭,表單值,或者查詢字符串的形式來模擬這些瀏覽器不支持的HTTP方法。每種支持RESTful Web服務的框架都有它們自己的實現方式,今天我將使用HttpModule也來模擬這個操作。最終的結果是可以直接訪問HttpRequest.HttpMethod獲取這些操作的方法名字。

實現原理:訂閱管線中的BeginRequest事件,檢查當前請求是否需要修改HttpMethod,如果是,則修改HttpMethod屬性。

所以選擇BeginRequest這個事件,是因為這個事件比較早,可以讓請求的后續階段都能讀到新的結果。

/// <summary>  /// 【演示用】實現了模擬更多 HttpMethod 的Module  /// </summary>  internal class XHttpMethodModule : IHttpModule  {  private FieldInfo _field;   public void Init(HttpApplication context)  {  // 訂閱這個較早的事件,可以讓請求的后續階段都能讀到新的結果。  context.BeginRequest += new EventHandler(context_BeginRequest);  _field = typeof(HttpRequest).GetField("_httpMethod", BindingFlags.Instance | BindingFlags.NonPublic);  }  void context_BeginRequest(object sender, EventArgs e)  {  HttpApplication app = (HttpApplication)sender;  // 這里僅檢查是否為POST操作,如果您的應用中需要使用GET來模擬的,請修改這里。  if( string.Equals(app.Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ) {  // 這里為了簡單,我只檢查請求頭,如果還需要檢查表單值或者查詢字符串,請修改這里。  string headerOverrideValue = app.Request.Headers["X-HTTP-Method-Override"];  if( string.IsNullOrEmpty(headerOverrideValue) == false ) {  if( string.Equals(headerOverrideValue, "GET", StringComparison.OrdinalIgnoreCase) == false &&  string.Equals(headerOverrideValue, "POST", StringComparison.OrdinalIgnoreCase) == false ) {  // HttpRequest.HttpMethod屬性其實就是訪問_httpMethod這個私有字段,我將直接修改它。  // 這樣修改后,最原始的HTTP方法就丟失,通常這或許也是可以接受的。  _field.SetValue(app.Request, headerOverrideValue.ToUpper());  }  }  }  }

我認為采用HttpModule來處理這個問題是個不錯的選擇。它至少有2個好處:

1. 這個HttpModule能繼續給其它的網站項目使用,因此提高了代碼的重用性。

2. 我可以隨時決定要不要支持模擬,不需要模擬時,從web.config中不加載它就可以了,因此切換很靈活,且不需要修改現有代碼。

來看一下頁面及調用結果吧

protected void Page_Load(object sender, EventArgs e)  {  Response.Write(Request.HttpMethod);  }

怎么用ASP.NET寫服務框架

調用結果如下:

怎么用ASP.NET寫服務框架

實戰演示 - URL重寫

使用HttpModule來實現URL重寫。這個功能應該是HttpModule非常經典的應用了。

通常情況下,這種應用常用的方式是將一個URL: /product/12 重寫為 /product.aspx?id=12 ,此時product.aspx應該是一個已經已存在的頁面。顯然重寫后的地址更友好。URL重寫的目的就是能讓URL更友好。

實現原理:訂閱管線的PostAuthorizeRequest事件,檢查URL是不是期望修改的模式,如果是,則調用Context.RewritePath()完成URL的重寫操作。在管線的后續處理中,最終會使用新的URL來映射到一個合適的HttpHandler。說明:選擇的事件只要在【第10個事件】之前就可以了,因為在第10個事件前重寫URL,才能保證到將請求映射到合適的處理器來執行。就這么簡單,請參考以下代碼:

public class MyServiceUrlRewriteModule : IHttpModule  {  // 為了演示簡單,直接寫死地址。   // 注意:MyService.axd 必須在web.config中注冊,以保證它能成功映射。  public static string RewriteUrlPattern = "/MyService.axd?sc={1}&op={1}";  public void Init(HttpApplication app)  {  app.PostAuthorizeRequest += new EventHandler(app_PostAuthorizeRequest);  }  void app_PostAuthorizeRequest(object sender, EventArgs e)  {  HttpApplication app = (HttpApplication)sender;  // 這里將檢查URL是否為需要重寫的模式,比如:  // http://localhost:11647/service/OrderService/QueryOrder  NamesPair pair = FrameworkRules.ParseNamesPair(app.Request);  if( pair == null )  return;  // 開始重寫URL,***將會映射到MyServiceHandler  int p = app.Request.Path.IndexOf('?');  if( p >0 )  app.Context.RewritePath(string.Format(RewriteUrlPattern, pair.ServiceName, pair.MethodName)  + "&" + app.Request.Path.Substring(p + 1)  );  else app.Context.RewritePath(string.Format(RewriteUrlPattern, pair.ServiceName, pair.MethodName));  }

重寫發生了什么?

對于一個傳入請求:http://localhost:11647/service/FormDemoService/ShowUrlInfo

它將被重寫為:http://localhost:11647/MyService.axd?sc=FormDemoService&op=ShowUrlInfo

由于在web.config中,對MyService.axd已做過注冊,因此ASP.NET會將請求轉交給注冊的處理器來處理它。

注意:URL重寫,會影響某些變量的值。請參考以下代碼,我將寫個服務方法來檢測這個現象:

[MyServiceMethod]

public string ShowUrlInfo(int a)

{

System.Web.HttpRequest request = System.Web.HttpContext.Current.Request;

System.Text.StringBuilder sb = new System.Text.StringBuilder();

sb.AppendFormat("Path: {0} ", request.Path);

sb.AppendFormat("RawUrl: {0} ", request.RawUrl);

sb.AppendFormat("Url.PathAndQuery: {0} ", request.Url.PathAndQuery);

return sb.ToString();

}

輸出結果:

怎么用ASP.NET寫服務框架

實戰演示 - URL路由

使用HttpModule來實現URL路由。這個功能隨著ASP.NET MVC框架的出現也逐漸流行起來了。

URL路由的目標也是為了使用URL更友好,與URL重寫類似。

實現原理:訂閱管線的PostResolveRequestCache事件,檢查URL是不是期望的路由模式,如果是,則要根據請求中所包含的信息找到一個合適的處理器,并臨時保存這個處理器,重寫URL到一個ASP.NET能映射處理器的地址。在管線的PostMapRequestHandler中,檢查前面有沒有臨時保存的處理器,如果有,則重新給Context.Handler賦值,并重寫URL到原始地址。在管線的后續處理中,最終會使用Context.Handler的HttpHandler。就這么簡單,請參考以下代碼:

public class MyServiceUrlRoutingModule : IHttpModule

{

private static readonly object s_dataKey = new object();

public void Init(HttpApplication app)

{

app.PostResolveRequestCache += new EventHandler(app_PostResolveRequestCache);

app.PostMapRequestHandler += new EventHandler(app_PostMapRequestHandler);

}

private void app_PostResolveRequestCache(object sender, EventArgs e)

{

HttpApplication app = (HttpApplication)sender;

// 獲取合適的處理器,注意這是與URL重寫的根本差別。

// 即:根據當前請求【主動】尋找一個處理器,而不是使用RewritePath讓ASP.NET替我們去找。

MyServiceHandler handler = GetHandler(app.Context);

if( handler == null )

return;

// 臨時保存前面獲取到的處理器,這個值將在PostMapRequestHandler事件中再取出來。

app.Context.Items[s_dataKey] = handler;

// 進入正常的MapRequestHandler事件,隨便映射到一個處理器就行了。

app.Context.RewritePath("~/MyServiceUrlRoutingModule.axd");

}

private void app_PostMapRequestHandler(object sender, EventArgs e)

{

HttpApplication app = (HttpApplication)sender;

// 取出在PostResolveRequestCache事件中獲得的處理器

MyServiceHandler handler = (MyServiceHandler)app.Context.Items[s_dataKey];

if( handler != null ) {

// 還原URL請求地址。注意這里和URL重寫的差別。

app.Context.RewritePath(app.Request.RawUrl);

// 還原根據GetHandler(app.Context)調用得到的處理器。

// 因為此時app.Context.Handler是由"~/MyServiceUrlRoutingModule.axd"映射得到的。

app.Context.Handler = handler;

}

}

注意:在MyServiceUrlRoutingModule中,我將請求【路由】到一個MyServiceHandler的實例,而不是讓ASP.NET根據URL來替我選擇。

在URL重寫的演示中,有些URL相關的屬性發生了改變,我們再來看一下URL路由是個什么結果:

怎么用ASP.NET寫服務框架

實現自己的服務框架

本篇博客在開頭說過:將在本次博客中改進上次的服務實現,讓它成為一個真正能用的服務框架。

前面在講述ASP.NET管線時,給出了很多示例代碼,這些示例代碼都可以在博客的結尾處下載到。這些代碼來源于【我的服務框架】中的部分源代碼,下面我將重點介紹【我的服務框架】。

利用【我的服務框架】將類公開成服務

在【我的服務框架】中,一個類要想公開為服務類,并不需要繼承某個類或者實現什么接口,只需要在類上加一個特性就好了,方法也只需加一個特性,示例代碼如下:

[MyService]

public class OrderService

{

[MyServiceMethod]

public static string Hello(string name)

{

return "Hello " + name;

}

[MyServiceMethod]

public List<Order>QueryOrder(QueryOrderCondition query)

{

// 模擬查詢過程,這里就直接返回一個列表。

List<Order>list = new List<Order>();

for( int i = 0;i <10;i++ )

list.Add(DataFactory.CreateRandomOrder());

return list;

}

public string HiddenMethod(string aa)

{

// 這個方法應該是不能以服務方式被調用到的。

throw new NotImplementedException();

}

}

如果某個方法需要只公開給登錄用戶或者指定的用戶,還可以使用以下方式:

// 這是一個訪問受限的服務類,只允許某些用戶調用。

[Authorize]

[MyService]

public static class LimitService

{

[Authorize(Users="fish-li, cc")]

[MyServiceMethod]

public static string CalcPassword(string pwd)

{

// 這個方法只能由 fish-li, cc 二個用戶來調用

if( pwd == null )

pwd = string.Empty;

byte[] buffer = (new MD5CryptoServiceProvider()).ComputeHash(Encoding.Default.GetBytes(pwd));

return BitConverter.ToString(buffer).Replace("-", "");

}

[MyServiceMethod]

public static string CalcBase64(string str)

{

// 這個方法只能由已登錄用戶調用。

if( string.IsNullOrEmpty(str) )

return string.Empty;

return Convert.ToBase64String(Encoding.UTF8.GetBytes(str));

}

}

就這么簡單,一個類,就可以成為一個服務。

說明:本框架并不要求將服務類在網站項目中實現,完全可以放在類庫中實現。

還可以支持Session哦。

[MyService(SessionMode=SessionMode.Support)]

public class SessionDemoService

{

[MyServiceMethod]

public int Add(int a)

{

// 一個累加的方法,檢驗是否可以訪問Session

if( System.Web.HttpContext.Current.Session == null )

throw new InvalidOperationException("Session沒有開啟。");

object obj = System.Web.HttpContext.Current.Session["counter"];

int counter = (obj == null ? 0 : (int)obj);

counter += a;

System.Web.HttpContext.Current.Session["counter"] = counter;

return counter;

}

}

SessionMode的定義如下:

public enum SessionMode

{

NotSupport,

Support,

ReadOnly

}

【我的服務框架】支持的序列化的種類

在上篇博客中,我演示了使用JSON序列化的做法來實現一個服務響應。本來也是打算讓框架僅支持JSON序列化的,因為傳輸的數據量小嘛。沒想到,做到后來,還是認為有必要把XML序列化也加進來,XML序列化快呀。***,居然想到既然是服務框架,Ajax調用也能算是服務吧,總不能不支持吧,后來干脆也能支持部分的Ajax調用了。

在【我的服務框架】中,服務端判斷客戶端發送的數據序列化方式是通過判斷請求頭"Serializer-Format"來實現的。序列化的種類還允許繼續自定義。只要實現以下接口:

public interface ISerializerProvider

{

object Deserialize(Type destType, HttpRequest request);

void Serializer(object obj, HttpResponse response);

}

然后調用以下方法就可以了:

public static class SerializerProviderFactory

{

public static void RegisterSerializerProvider(string name, Type type)

{

// ...................................

}

判斷客戶端的序列化方式,由屬性FrameworkRules.GetSerializerFormat來決定:

public static class FrameworkRules

{

private static string Internal_GetSerializerFormat(HttpRequest request){

string flag = request.Headers["Serializer-Format"];

return (string.IsNullOrEmpty(flag) ? "form" : flag);

}

private static Func<HttpRequest, string>_serializerFormatRule = Internal_GetSerializerFormat;

/// <summary>

/// 此委托用來判斷客戶端發起的請求中,數據是以什么方式序列化的。

/// 返回的結果將會交給SerializerProviderFactory.GetSerializerProvider()來獲取序列化提供者

/// 默認的實現是檢查請求頭:"Serializer-Format"

/// </summary>

public static Func<HttpRequest, string>GetSerializerFormat

{

internal get { return _serializerFormatRule; }

set

{

if( value == null )

throw new ArgumentNullException("value");

_serializerFormatRule = value;

}

}

只是一個委托,可以自己重新實現。

目前本框架提供了三個實現了接口ISerializerProvider的類供用戶使用:JsonSerializerProvider, XmlSerializerProvider, FormSerializerProvider

這里只展示JsonSerializerProvider的實現:

internal class JsonSerializerProvider : ISerializerProvider

{

private static readonly MethodInfo s_JSSDeserializeMI

= typeof(JavaScriptSerializer).GetMethod("Deserialize");

JavaScriptSerializer jss = new JavaScriptSerializer();

public object Deserialize(Type destType, HttpRequest request)

{

StreamReader sr = new StreamReader(request.InputStream, request.ContentEncoding);

string input = sr.ReadToEnd();

MethodInfo deserialize = s_JSSDeserializeMI.MakeGenericMethod(destType);

return deserialize.Invoke(jss, new object[] { input });

}

public void Serializer(object obj, HttpResponse response)

{

if( obj == null )

return;

response.ContentType = "application/json";

response.Write(jss.Serialize(obj));

}

注意:FormSerializerProvider的實現不夠完善,因為再搞下去,就和【我的WEB框架】就重復了。有興趣的自己去完善吧。

這里再給自己的作品打個廣告:

【ASP.NET MVC 框架,我也來山寨一下】, 【曬曬我的Ajax服務端框架】, 【我的Ajax服務端框架 - (1) JS直接調用C#方法】

【我的服務框架】對gzip的支持

對于gzip的支持,我只想說:太簡單了。

前面不是已給出DuplexGzipModule的實現代碼嘛。是的,就是把它注冊到web.config中就可以了。

你說簡不簡單? 完全不用寫多余的代碼,要不要gzip支持,也只是個配置問題!

說到這里,我想起前段時間Artech寫的一篇博客通過WCF擴展實現消息壓縮,正如我在前篇博客的回復中說到的:“本來真沒興趣看的,不過,為了驗證我的猜想,還是去看了一下,果然也沒讓我失望。”。

在此,有必要公開一下我的想法:絕對沒有半點看不起Artech的意思,只是我對WCF沒有興趣了。理由也簡單:不夠簡單。

還是接著說,Artech的博客展示了在WCF中壓縮消息的方式,當然我相信Artech對于WCF的理解,他的方案或許應該是最簡單的解決方案,但是和【我的服務框架】對gzip的支持的易用性根本沒法比。

WCF的粉絲們,當您看到這里,請先別忙著噴我。聽我說完:WCF的確很強大,我的這個不到700行的框架那也是根本不能和它相比的。

做這個比較僅僅是為了展示ASP.NET是一個強大的平臺,ASP.NET有更高水準的擴展性。

利用【我的服務框架】發布服務的5種方式

【我的服務框架】可以提供5種不同的方式,讓您將一個類及方法公開成一個服務,供外界調用。

方法1:使用DirectProcessRequestMoudle,只需要配置web.config即可。

<httpModules>

<add name="DirectProcessRequestMoudle" type="MySimpleServiceFramework.DirectProcessRequestMoudle"/>

</httpModules>

客戶端調用URL:http://localhost:11647/service/OrderService/QueryOrder

說明:URL模式是可以自由定義的,只要給FrameworkRules.ParseNamesPair賦值即可,它的定義如下:

public static Func<HttpRequest, NamesPair>ParseNamesPair

默認的實現方式:

internal static class UrlPatternHelper

{

// 為了演示簡單,我只定義一個URL模式。【因為我認為對于服務來說,一個就夠了】

// 如果希望適用性更廣,可以從配置文件中讀取,并且可支持多組URL模式。

// URL中加了"/service/"只是為了能更好地區分其它請求,如果您的網站沒有子目錄,刪除它也是可以的。

private static readonly string UrlPattern = @"/service/(?<name>[^/]+)/(?<method>[^/]+)[/?]?";

public static NamesPair ParseNamesPair(HttpRequest request)

{

if( request == null )

throw new ArgumentNullException("request");

MatchCollection matchs = Regex.Matches(request.Path, UrlPattern);

if( matchs.Count != 1 )

return null;

Match m = matchs[0];

return new NamesPair {

ServiceName = m.Result("${name}"),

MethodName = m.Result("${method}")

};

}

客戶端調用URL: http://localhost:11647/service/OrderService/QueryOrder

方法2:使用MyServiceUrlRoutingModule,只需要配置web.config即可。

<httpModules>

<add name="MyServiceUrlRoutingModule" type="MySimpleServiceFramework.MyServiceUrlRoutingModule"/>

</httpModules>

客戶端調用URL: http://localhost:11647/service/OrderService/QueryOrder

說明:只有這種方式才能支持Session

方法3:使用MyServiceUrlRewriteModule,只需要配置web.config即可。

<httpHandlers>

<add path="/MyService.axd" verb="*" validate="false" type="MySimpleServiceFramework.MyServiceHandler"/>

</httpHandlers>

<httpModules>

<add name="MyServiceUrlRewriteModule" type="MySimpleServiceFramework.MyServiceUrlRewriteModule"/>

</httpModules>

客戶端調用URL: http://localhost:11647/service/OrderService/QueryOrder

方法4:使用MyServiceHandler,只需要配置web.config即可。

<httpHandlers>

<add path="/MyService.axd" verb="*" validate="false" type="MySimpleServiceFramework.MyServiceHandler"/>

</httpHandlers>

客戶端調用URL: http://localhost:11647/MyService.axd?sc=OrderService&op=QueryOrder

方法5:創建一個ashx,不需要任何配置。

<%@ WebHandler Language="C#" Class="MyService" %>

using System;

using System.Web;

using MySimpleServiceFramework;

public class MyService : IHttpHandler {

public void ProcessRequest (HttpContext context) {

NamesPair pair = new NamesPair();

pair.ServiceName = context.Request.QueryString["sc"];

pair.MethodName = context.Request.QueryString["op"];

ServiceExecutor.ProcessRequest(context, pair);

}

public bool IsReusable {

get {

return false;

}

}

}

客戶端調用URL: http://localhost:11647/MyService.ashx?sc=OrderService&op=QueryOrder

注意:前三種方法,需要在IIS中做些額外的配置,因為URL中不包含文件擴展名了,IIS不知道把請求交給ASP.NET來處理。

具體配置見下圖,此處省略78個字。

怎么用ASP.NET寫服務框架

我對發布服務的5種方式的建議

雖然,我給出了5種發布方式,但是我還是想說說我個人的想法。

在這些方法中,使用URL重寫,URL路由的方法,并不是我想推薦的,寫它們是主要是為了展示HttpModule 。不推薦它們是因為它們要判斷URL是否符合指定模式,這個判斷是有成本的。至于成本有多高,特此,我專了做門的測試。在示例代碼壓縮包中有個___TestRoutePerformance目錄,結果如何,還是您自己去看吧,我也有點累了。

此外,我想問:對于服務來說,URL友好有多大意義?服務的URL會讓用戶來輸入還是讓Google的爬蟲來訪問?

如果以上二個問題都是否定的,那么,這二種方法就是在白白浪費機器的性能了。

當然了,如果您的站點訪問量不大,那么這點性能也可以忽略不計了,就當我沒說。

使用URL重寫URL路由,還有個比較麻煩的事情:如果想通過URL多傳遞一個參數,那么,是不是又要修改URL模式?

對于使用DirectProcessRequestMoudle這種模式,我以前已經說過了:除非要很好的理由,否則不建議使用這種方法。

至于其它的二種方式,本質上是一樣的,只是說:處理器誰來寫的差別了。

不過,如果您要是選擇手工創建一個處理器,除了不用修改web.config之外,還可以自定義URL參數名,可以選擇要不要支持Session

【我的服務框架】的一些核心類

ReflectionHelper類用于根據類名及服務名定位到一個服務類型以及要調用的方法。

因此,它在框架中的作用也是非常關鍵的。

internal static class ReflectionHelper

{

private static List<TypeAndAttrInfo>s_typeList;

static ReflectionHelper()

{

InitServiceTypes();

}

/// <summary>

/// 加載所有的服務類型,判斷方式就是檢查類型是否有MyServiceAttribute

/// </summary>

private static void InitServiceTypes()

{

s_typeList = new List<TypeAndAttrInfo>(256);

ICollection assemblies = BuildManager.GetReferencedAssemblies();

foreach( Assembly assembly in assemblies ) {

try {

(from t in assembly.GetExportedTypes()

let a = (MyServiceAttribute[])t.GetCustomAttributes(typeof(MyServiceAttribute), false)

where a.Length >0

select new TypeAndAttrInfo {

ServiceType = t, Attr = a[0], AuthorizeAttr = t.GetClassAuthorizeAttribute() }

).ToList().ForEach(b => s_typeList.Add(b));

}

catch { }

}

}

private static AuthorizeAttribute GetClassAuthorizeAttribute(this Type t)

{

AuthorizeAttribute[] attrs = (AuthorizeAttribute[])t.GetCustomAttributes(typeof(AuthorizeAttribute), false);

return (attrs.Length >0 ? attrs[0] : null);

}

/// <summary>

/// 根據一個名稱獲取對應的服務類型(從緩存中獲取類型)

/// </summary>

/// <param name="typeName"></param>

/// <returns></returns>

private static TypeAndAttrInfo GetServiceType(string typeName)

{

if( string.IsNullOrEmpty(typeName) )

throw new ArgumentNullException("typeName");

// 查找類型的方式:如果有點號,則按全名來查找(包含命名空間),否則只看名字。

// 本框架對于多個匹配條件的類型,將返回***個匹配項。

if( typeName.IndexOf('.') >0 )

return s_typeList.FirstOrDefault(t => string.Compare(t.ServiceType.FullName, typeName, true) == 0);

else

return s_typeList.FirstOrDefault(t => string.Compare(t.ServiceType.Name, typeName, true) == 0);

}

private static Hashtable s_methodTable = Hashtable.Synchronized(

new Hashtable(4096, StringComparer.OrdinalIgnoreCase));

/// <summary>

/// 根據指定的類型以及方法名稱,獲取對應的方法信息

/// </summary>

/// <param name="type"></param>

/// <param name="methodName"></param>

/// <returns></returns>

private static MethodAndAttrInfo GetServiceMethod(Type type, string methodName)

{

if( type == null )

throw new ArgumentNullException("type");

if( string.IsNullOrEmpty(methodName))

throw new ArgumentNullException("methodName");

// 首先嘗試從緩存中讀取

string key = methodName + "@" + type.FullName;

MethodAndAttrInfo mi = (MethodAndAttrInfo)s_methodTable[key];

if( mi == null ) {

// 注意:這里不考慮方法的重載。

MethodInfo method = type.GetMethod(methodName,

BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);

if( method == null )

return null;

MyServiceMethodAttribute[] attrs = (MyServiceMethodAttribute[])

method.GetCustomAttributes(typeof(MyServiceMethodAttribute), false);

if( attrs.Length != 1 )

return null;

// 由于服務方法的參數來源于反序列化,此時只可能包含一個參數。

ParameterInfo[] paraInfos = method.GetParameters();

if( paraInfos.Length != 1 )

throw new ArgumentNullException("指定的方法雖找到,但該方法的參數數量不是1");

AuthorizeAttribute[] auths = (AuthorizeAttribute[])method.GetCustomAttributes(typeof(AuthorizeAttribute), false);

mi = new MethodAndAttrInfo {

MethodInfo = method,

ParamType = paraInfos[0].ParameterType,

Attr = attrs[0],

AuthorizeAttr = (auths.Length >0 ? auths[0] : null)

};

s_methodTable[key] = mi;

}

return mi;

}

/// <summary>

/// 根據類型名稱以及方法名稱返回要調用的相關信息

/// </summary>

/// <param name="pair">包含類型名稱以及方法名稱的對象</param>

/// <returns></returns>

public static InvokeInfo GetInvokeInfo(NamesPair pair)

{

if( pair == null )

throw new ArgumentNullException("pair");

InvokeInfo vkInfo = new InvokeInfo();

vkInfo.ServiceTypeInfo = GetServiceType(pair.ServiceName);

if( vkInfo.ServiceTypeInfo == null )

return null;

vkInfo.MethodAttrInfo = GetServiceMethod(vkInfo.ServiceTypeInfo.ServiceType, pair.MethodName);

if( vkInfo.MethodAttrInfo == null )

return null;

if( vkInfo.MethodAttrInfo.MethodInfo.IsStatic == false )

vkInfo.ServiceInstance = Activator.CreateInstance(vkInfo.ServiceTypeInfo.ServiceType);

return vkInfo;

}

}

ServiceExecutor用于調用服務方法,前面所說的5種服務發布方式,最終都要經過這里。

/// <summary>

/// 最終調用服務方法的工具類。

/// </summary>

public static class ServiceExecutor

{

internal static void ProcessRequest(HttpContext context, ServiceInfo info)

{

if( context == null )

throw new ArgumentNullException("context");

if( info == null || info.InvokeInfo == null )

throw new ArgumentNullException("info");

//if( context.Request.InputStream.Length == 0 )

// throw new InvalidDataException("沒有調用數據,請將調用數據以請求體的方式傳入。");

if( info.InvokeInfo.AuthenticateRequest(context) == false )

ExceptionHelper.Throw403Exception(context);

// 獲取客戶端的數據序列化格式。

// 默認實現方式:request.Headers["Serializer-Format"];

// 注意:這是我自定義的請求頭名稱,也可以不指定,默認為:form (表單)

string serializerFormat = FrameworkRules.GetSerializerFormat(context.Request);

ISerializerProvider serializerProvider =

SerializerProviderFactory.GetSerializerProvider(serializerFormat);

// 獲取要調用方法的參數類型

Type destType = info.InvokeInfo.MethodAttrInfo.ParamType;

// 獲取要調用的參數

context.Request.InputStream.Position = 0;// 防止其它Module讀取過,但沒有歸位。

object param = serializerProvider.Deserialize(destType, context.Request);

// 調用服務方法

object result = info.InvokeInfo.MethodAttrInfo.MethodInfo.Invoke(

info.InvokeInfo.ServiceInstance, new object[] { param });

// 寫輸出結果

if( result != null )

serializerProvider.Serializer(result, context.Response);

}

/// <summary>

/// 【外部接口】用于根據服務的類名和方法名執行某個請求

/// </summary>

/// <param name="context"></param>

/// <param name="pair"></param>

public static void ProcessRequest(HttpContext context, NamesPair pair)

{

if( pair == null )

throw new ArgumentNullException("pair");

if( string.IsNullOrEmpty(pair.ServiceName) || string.IsNullOrEmpty(pair.MethodName) )

ExceptionHelper.Throw404Exception(context);

InvokeInfo vkInfo = ReflectionHelper.GetInvokeInfo(pair);

if( vkInfo == null )

ExceptionHelper.Throw404Exception(context);

ServiceInfo info = new ServiceInfo(pair, vkInfo);

ProcessRequest(context, info);

}

}

到此,關于“怎么用ASP.NET寫服務框架”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

保康县| 和平区| 正阳县| 浪卡子县| 保定市| 海口市| 溧阳市| 和平区| 南乐县| 应用必备| 商洛市| 武夷山市| 青河县| 宁化县| 石景山区| 乐平市| 芦山县| 海兴县| 英山县| 蓝田县| 保德县| 海林市| 宝丰县| 龙川县| 肇庆市| 化州市| 沐川县| 开封市| 花垣县| 乐安县| 溧水县| 米泉市| 安仁县| 卫辉市| 湘西| 宽城| 聂拉木县| 开江县| 本溪| 南平市| 富民县|