9

WCFに実装され、AzureでホストされているRESTサービスの認証を実装しようとしています。HttpModuleを使用して、AuthenticationRequest、PostAuthenticationRequest、およびEndRequestイベントを処理しています。Authorizationヘッダーがない場合、またはそこに含まれるトークンが無効である場合、EndRequest中にResponseのStatusCodeを401に設定しています。ただし、EndRequestが2回呼び出され、2回目の呼び出しで応答にすでにヘッダーが含まれていると判断しました。 setを実行すると、StatusCodeを設定するコードが例外をスローします。

Init()にロックを追加して、ハンドラーが2回登録されないようにしました。まだ2回実行されました。Init()も2回実行され、HttpModuleの2つのインスタンスが作成されていることを示しています。ただし、VSデバッガーでSet Object IDを使用すると、要求が実際には異なる要求であることが示されているようです。Fiddlerで、ブラウザーからサービスに発行されているリクエストが1つしかないことを確認しました。

WCFサービスホストの構成に依存する代わりにglobal.asaxルーティングを使用するように切り替えると、ハンドラーは1回だけ呼び出され、すべてが正常に機能します。

system.web構成セクションとWeb.configのsystem.webServer構成セクションに構成を追加すると、ハンドラーは1回だけ呼び出され、すべてが正常に機能します。

ですから、私には緩和策がありますが、私は理解できない行動が本当に嫌いです。ハンドラーが2回呼び出されるのはなぜですか?

問題の最小限の再現は次のとおりです。

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;
        }
    }
}
4

2 に答える 2

8

ASP.net アプリケーションが起動すると、パフォーマンスを最大化するために、ASP.NET ワーカー プロセスは必要な数のHttpApplicationオブジェクトをインスタンス化します。各HttpApplicationオブジェクトは、登録されているそれぞれのコピーを 1 つインスタンス化IHttpModuleし、Init メソッドを呼び出します! これは、IIS (または Web サーバーに組み込まれた VS である cassini) で実行される ASP.NET プロセスの内部設計です。ASPX ページには、ブラウザーがダウンロードしようとする他のリソース、外部リソース、iframe、css ファイル、または ASP.NET ワーカー プロセスの動作へのリンクが含まれている可能性があります。

幸いなことに、Global.asax には当てはまりません。

これはMSDNからのものです:

Application_Start および Application_End メソッドは、HttpApplication イベントを表さない特別なメソッドです。ASP.NET は、各 HttpApplication インスタンスではなく、アプリケーション ドメインの有効期間中に 1 回呼び出します。

ただしHTTPModule's init、すべてのモジュールが作成された後、メソッドは HttpApplication クラスのインスタンスごとに 1 回呼び出されます。

アプリケーションで ASP.NET ページまたはプロセスが初めて要求されると、HttpApplication の新しいインスタンスが作成されます。ただし、パフォーマンスを最大化するために、HttpApplication インスタンスは複数の要求で再利用される場合があります。

そして、次の図で説明します。 ここに画像の説明を入力

一度だけ実行されることが保証されているコードが必要な場合は、 を使用するかApplication_StartGlobal.asaxフラグを設定して、基になるモジュールでロックすることができます。

于 2012-11-03T09:56:47.870 に答える
2

申し訳ありませんが、2 回呼び出される理由はわかりませんが、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           
            //.............         
        }
    }
于 2012-11-07T12:32:11.807 に答える