2012年10月17日 星期三

系統Memo - IHttpModule 中使用Session State

這個問題其實挺弔詭的,沒有什麼文件清楚的寫出來(該死的微軟)
要在IHttpModule中使用Session有非常多的限制:

1. 網路上眾說紛紜,有人說只在AcquireRequestState和PreRequestHandlerExecute兩個事件中,才有辦法使用Session,但也有人說http module根本沒辦法使用session,造成不少困擾。

2. 經測試,即使是在上述兩個事件中,HttpApplication.Context.Session依然為null,我的猜測是這樣的方法只適用於.aspx之類的http request,其餘一律行不通。(我發的http request是抓.png或是manifest這類的行為)

最後採用的解法是:
http://forums.asp.net/t/1098574.aspx/1

我看不太懂裡面針對handler物件繞來繞去的原因是什麼,只大概猜說是要自己宣告一個Handler類別,產生handler物件,然後用原本的Application.Context.Handler傳進來,藉此啟動SessionStateModule,讓SessionState成為avaliable狀態。

以下只擷取相關部分,RequestBegin之類的事件省略,
Code snippet:


public void Init(HttpApplication application)
{
    // following two event handler are both required
    application.PostAcquireRequestState += new EventHandler(OnPostAcquireRequestState);
    application.PostMapRequestHandler += new EventHandler(OnPostMapRequestHandler);
}


/**
* for the usage of SessionState, replace the original handler with ours, force SessionState to be valiable
**/
public void OnPostMapRequestHandler(Object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication)sender;

    if (application.Context.Handler is IReadOnlySessionState || application.Context.Handler is IRequiresSessionState)
    {
        // no need to replace the original handler
        return;
    }

    // swap the original handler
    application.Context.Handler = new TempHandler(application.Context.Handler);
}


public void OnPostAcquireRequestState(Object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication)sender;
    TempHandler handler = HttpContext.Current.Handler as TempHandler;

     if (handler != null)
     {
         HttpContext.Current.Handler = handler.myHandler;
     }

    // from this moment, SessionState is valiable
    HttpSessionState session = application.Context.Session;

}


/**
* A temp handler used to force the SessionStateModule to load session state
**/
public class TempHandler : IHttpHandler, IRequiresSessionState
{
    internal readonly IHttpHandler myHandler; // store original application context handler

    public TempHandler(IHttpHandler originalhttpHandler)
    {
        this.myHandler = originalhttpHandler;
    }

    public void ProcessRequest(HttpContext context)
    {
        throw new InvalidOperationException("InvalidOperationException");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

另外,module類別除了繼承IHttpModule以外,必須再繼承IRequiresSessionState。

Anyway,這個方法讓我成功取到Session.SessionID,原理之後再慢慢思考。


沒有留言: