Asp.net HttpModule EndRequest处理程序调用了两次

Asp.net HttpModule EndRequest处理程序调用了两次,asp.net,wcf,azure,Asp.net,Wcf,Azure,我正在尝试为在WCF中实现并托管在Azure上的REST服务实施身份验证。我正在使用HttpModule处理AuthenticationRequest、PostAuthenticationRequest和EndRequest事件。如果授权标头丢失或其中包含的令牌无效,则在EndRequest期间,我将响应上的StatusCode设置为401。但是,我已经确定EndRequest被调用了两次,在第二次调用时,响应已经设置了头,导致设置StatusCode的代码抛出异常 我向Init()添加了锁,以

我正在尝试为在WCF中实现并托管在Azure上的REST服务实施身份验证。我正在使用HttpModule处理AuthenticationRequest、PostAuthenticationRequest和EndRequest事件。如果授权标头丢失或其中包含的令牌无效,则在EndRequest期间,我将响应上的StatusCode设置为401。但是,我已经确定EndRequest被调用了两次,在第二次调用时,响应已经设置了头,导致设置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;
        }
    }
}
使用系统;
使用System.Web;
名称空间TestWCFRole
{
公共类AuthModule:IHttpModule
{
/// 
///您需要在服务器的web.config文件中配置此模块
///在能够使用它之前,请访问web并向IIS注册。有关详细信息,请参阅
///请参阅以下链接:http://go.microsoft.com/?linkid=8101007
/// 
#区域IHTTP模块成员
公共空间处置()
{
//这里有清理代码。
}
公共void Init(HttpApplication上下文)
{
//下面是如何处理LogRequest事件并提供
//it的自定义日志记录实现
context.EndRequest+=新事件处理程序(OnEndRequest);
}
#端区
public void OnEndRequest(对象源、事件参数)
{
HttpContext.Current.Response.StatusCode=401;
}
}
}

当ASP.net应用程序启动时,为了最大限度地提高性能,ASP.net工作进程将根据需要实例化尽可能多的
HttpApplication
对象。每个
HttpApplication
对象还将实例化已注册的每个
IHttpModule
的一个副本,并调用Init方法!这实际上是在IIS(或cassini,即VS内置Web服务器)下运行的ASP.NET进程的内部设计。可能是因为您的ASPX页面包含指向浏览器将尝试下载的其他资源的链接、外部资源、iframe、css文件,或者ASP.NET工作进程行为

幸运的是,Global.asax并非如此:

:

应用程序开始和应用程序结束方法是特殊方法 不代表HttpApplication事件的。ASP.NET只调用它们一次 针对应用程序域的生命周期,而不是针对每个应用程序域 HttpApplication实例

但是,在创建所有模块后,对HttpApplication类的每个实例调用一次HTTPModule的init方法

第一次在应用程序中请求ASP.NET页或进程时 应用程序,创建一个新的HttpApplication实例。但是, 为了最大限度地提高性能,HttpApplication实例可能会重新用于 多个请求

如下图所示:


如果您想要保证只运行一次的代码,您可以使用
Global.asax的
Application\u Start
,或者设置一个标志并将其锁定在底层模块中,这对于身份验证来说不是一个好的做法

很抱歉,不知道为什么可以调用两次EndRequest,但是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           
            //.............         
        }
    }

您是否在应用程序中使用URLEwrite?看起来,它会导致EndRequest触发两次。可以在后台启用它吗?我的web.config中没有URLEwriteModule,我想没有。这真的很奇怪,因为我不能说我所有的问题都是由URLEwriteModule引起的。但其中一些是。我只希望设置
runAllManagedModulesForAllRequests=“true”
将导致为每个请求执行自定义模块,包括静态文件(JS/CSS/Images)。因此,对于我来说,一次页面加载两次似乎太少了;问题是,对于服务的单个请求,我看到两个EndRequests。它不应该是JS/CSS/Images,因为这是一个服务,而不是一个网页。我从Fiddler身上可以看出,他只提出了一个请求。问题是为什么我通过Fiddler看到一个请求,但在模块中看到两个请求,以及如果我在web.config中复制配置,为什么这个问题会消失。通常情况下,如果您使用IIS7+confi,您不需要复制配置