Могу ли я получить доступ к состоянию сеанса из HTTPModule?
Я действительно мог бы сделать с обновлением переменных сеанса пользователя из моего HTTPModule, но, насколько я вижу, это невозможно.
ОБНОВЛЕНИЕ: мой код в настоящее время выполняется внутри OnBeginRequest ()
обработчик события.
ОБНОВЛЕНИЕ: Следуя совету, полученному до сих пор, я попытался добавить это к Init ()
рутина в моем HTTPModule:
AddHandler context.PreRequestHandlerExecute, AddressOf OnPreRequestHandlerExecute
Но по моему OnPreRequestHandlerExecute
рутина, состояние сеанса все еще недоступно!
Спасибо и извинения, если я что-то упустил!
6 ответов
Нашел это на форумах ASP.NET:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regradless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
HttpContext.Current.Session должен просто работать, предполагая, что ваш HTTP-модуль не обрабатывает какие-либо конвейерные события, которые происходят до инициализации состояния сеанса...
РЕДАКТИРОВАТЬ, после уточнения в комментариях: при обработке события BeginRequest объект Session действительно все равно будет иметь значение null/Nothing, поскольку он еще не был инициализирован средой выполнения ASP.NET. Чтобы обойти это, переместите ваш код обработки к событию, которое происходит после PostAcquireRequestState - мне лично для этого нравится PreRequestHandlerExecute, так как вся работа низкого уровня в значительной степени выполняется на этом этапе, но вы по-прежнему препятствует любой нормальной обработке.
Доступ к HttpContext.Current.Session
в IHttpModule
можно сделать в PreRequestHandlerExecute
обработчик.
PreRequestHandlerExecute: "Происходит непосредственно перед тем, как ASP.NET начинает выполнять обработчик событий (например, страницу или веб-службу XML)". Это означает, что перед обслуживанием страницы aspx это событие выполняется. "Состояние сеанса" доступно, так что вы можете вырубить себя.
Пример:
public class SessionModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += BeginTransaction;
context.EndRequest += CommitAndCloseSession;
context.PreRequestHandlerExecute += PreRequestHandlerExecute;
}
public void Dispose() { }
public void PreRequestHandlerExecute(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
context.Session["some_sesion"] = new SomeObject();
}
...
}
Если вы пишете обычный, базовый HttpModule в управляемом приложении, который вы хотите применить к запросам asp.net через страницы или обработчики, вам просто нужно убедиться, что вы используете событие в жизненном цикле после создания сеанса. PreRequestHandlerExecute вместо Begin_Request, как правило, куда я иду. MDB имеет это право в своем редактировании.
Более длинный фрагмент кода, изначально указанный как ответ на вопрос, работает, но он сложнее и шире, чем первоначальный вопрос. Он будет обрабатывать случай, когда контент поступает от чего-то, у кого нет доступного обработчика ASP.net, где вы можете реализовать интерфейс IRequiresSessionState, тем самым вызывая механизм сеанса, чтобы сделать его доступным. (Как статический GIF-файл на диске). Это в основном установка фиктивного обработчика, который затем просто реализует этот интерфейс, чтобы сделать сеанс доступным.
Если вы просто хотите сеанс для своего кода, просто выберите правильное событие для обработки в вашем модуле.
Начиная с .NET 4.0, нет необходимости в этом взломе с IHttpHandler для загрузки состояния сеанса (как в ответе, получившем большинство голосов). Существует метод HttpContext.SetSessionStateBehavior для определения необходимого поведения сеанса. Если сеанс необходим для всех запросов, установленных
runAllManagedModulesForAllRequests
значение true в объявлении HttpModule в web.config, но имейте в виду, что выполнение всех модулей для всех запросов требует значительных затрат на производительность, поэтому обязательно используйте
preCondition="managedHandler"
если вам не нужен сеанс для всех запросов. Для будущих читателей вот полный пример:
Объявление web.config - вызов HttpModule для всех запросов:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess"/>
</modules>
</system.webServer>
Объявление web.config - вызов HttpModule только для управляемых запросов:
<system.webServer>
<modules>
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess" preCondition="managedHandler"/>
</modules>
</system.webServer>
Реализация IHttpModule:
namespace HttpModuleWithSessionAccess
{
public class ModuleWithSessionAccess : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += Context_BeginRequest;
context.PreRequestHandlerExecute += Context_PreRequestHandlerExecute;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if (app.Context.Session != null)
{
app.Context.Session["Random"] = $"Random value: {new Random().Next()}";
}
}
public void Dispose()
{
}
}
}
Попробуйте это: в классе MyHttpModule объявите:
private HttpApplication contextapp;
Затем:
public void Init(HttpApplication application)
{
//Must be after AcquireRequestState - the session exist after RequestState
application.PostAcquireRequestState += new EventHandler(MyNewEvent);
this.contextapp=application;
}
И так, в другом методе (событии) того же класса:
public void MyNewEvent(object sender, EventArgs e)
{
//A example...
if(contextoapp.Context.Session != null)
{
this.contextapp.Context.Session.Timeout=30;
System.Diagnostics.Debug.WriteLine("Timeout changed");
}
}