[英]HttpModule EndRequest handler called twice
我正在嘗試為在WCF中實現並托管在Azure上的REST服務實現身份驗證。 我正在使用HttpModule來處理AuthenticationRequest,PostAuthenticationRequest和EndRequest事件。 如果Authorization標頭丟失或者其中包含的標記無效,則在EndRequest期間我將響應的StatusCode設置為401.但是,我已確定調用EndRequest兩次,並且在第二次調用時響應已經有標題set,導致設置StatusCode的代碼拋出異常。
我向Init()添加了鎖,以確保處理程序沒有被注冊兩次; 還是跑了兩次。 Init()也運行了兩次,表明正在創建兩個HttpModule實例。 但是,在VS調試器中使用Set Object ID似乎表明請求實際上是不同的請求。 我在Fiddler中驗證過,瀏覽器只向我的服務發出了一個請求。
如果我切換到使用global.asax路由而不是依賴於WCF服務主機配置,則只調用一次處理程序,一切正常。
如果我將配置添加到system.web配置部分以及Web.config中的system.webServer配置部分,則只調用一次處理程序,一切正常。
所以我有緩解措施,但我真的不喜歡我不明白的行為。 為什么處理程序被調用兩次?
這是問題的最小重復:
Web.config文件:
<system.web>
<compilation debug="true" targetFramework="4.0" />
<!--<httpModules>
<add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/>
</httpModules>-->
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
<service name="TestWCFRole.Service1">
<endpoint binding="webHttpBinding" name="RestEndpoint" contract="TestWCFRole.IService1" bindingConfiguration="HttpSecurityBinding" behaviorConfiguration="WebBehavior"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/" />
</baseAddresses>
</host>
</service>
</services>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
<bindings>
<webHttpBinding>
<binding name="HttpSecurityBinding" >
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/>
</modules>
<directoryBrowse enabled="true"/>
</system.webServer>
Http模塊:
using System;
using System.Web;
namespace TestWCFRole
{
public class AuthModule : IHttpModule
{
/// <summary>
/// You will need to configure this module in the web.config file of your
/// web and register it with IIS before being able to use it. For more information
/// see the following link: http://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
// Below is an example of how you can handle LogRequest event and provide
// custom logging implementation for it
context.EndRequest += new EventHandler(OnEndRequest);
}
#endregion
public void OnEndRequest(Object source, EventArgs e)
{
HttpContext.Current.Response.StatusCode = 401;
}
}
}
當ASP.net應用程序啟動時,為了最大限度地提高性能,ASP.NET Worker進程將根據需要實例化多個HttpApplication
對象。 每個HttpApplication
對象還將實例化已注冊的每個IHttpModule
一個副本並調用Init方法! 這實際上是在IIS下運行的ASP.NET進程的內部設計(或cassini,它是在Web服務器中構建的VS)。 可能是因為您的ASPX頁面鏈接到瀏覽器將嘗試下載的其他資源,外部資源,iframe,css文件或ASP.NET Worker Process行為。
幸運的是,Global.asax的情況並非如此:
這是來自MSDN :
Application_Start和Application_End方法是不代表HttpApplication事件的特殊方法。 ASP.NET在應用程序域的生命周期內調用它們一次,而不是為每個HttpApplication實例調用它們。
但是,在創建所有模塊之后,為HttpApplication類的每個實例調用一次HTTPModule's init
方法
第一次在應用程序中請求ASP.NET頁面或進程時,會創建一個新的HttpApplication實例。 但是,為了最大限度地提高性能,HttpApplication實例可能會被重用於多個請求。
並通過下圖說明:
如果您希望保證只運行一次的代碼,您可以使用Global.asax
Application_Start
或設置一個標志並將其鎖定在底層模塊中,這對於身份驗證來說並不是一個好習慣!
對不起,為什么它可以被調用兩次沒有任何線索,但EndRequest最終可能因多種原因被調用。 請求完成,請求被中止,發生了一些錯誤。 因此,我不會相信假設如果你到達那里,你實際上有401,這可能是出於其他原因。
我只是將我的邏輯保留在AuthenticateRequest管道中:
public class AuthenticationModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.AuthenticateRequest += Authenticate;
}
public static void Authenticate(object sender, EventArgs e)
{
// authentication logic here
//.............
if (authenticated) {
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(myUser, myRoles);
}
// failure logic here
//.............
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.