我正在尝试为在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;
- }
- }
- }
解决方法
对不起,为什么它可以被调用两次没有任何线索,但是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
- //.............
- }
- }