Session not available in IHttpModule - c#

In my program I'm trying to use session variable in IHttpModule. Here is my code. This is working fine in VS 2010 development server. But when I try to debug in IIS7 it shows exception System.Web.HttpException: Session state is not available in this context
So why session not available in IIS 7 but in development server.
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
HttpApplication http_application = sender as HttpApplication;
http_application.Session["language"] = "test";
}
}

All you need is to implement IRequiresSessionState.
So your code should look like:
public class Globalizer : IHttpModule, IRequiresSessionState
if all you use is reading Sessions (and never update them) then you should use IReadOnlySessionState as this last do not lock the session state, and therefor, you will not have concurrent requests).

If possible, move your handler to a PostAcquireRequestState handler:
Occurs when the request state (for example, session state) that is associated with the current request has been obtained.
(Or an even later event)
The SessionStateModule itself loads session state in response to the AcquireRequestState event - So at the moment, you're depending on whether your module or the session state module's handlers fire first.

Found this over on the ASP.NET forums:
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; }
}
}
}

Found the reason.
If is because AcquireRequestState trigger for the all files such as CSS, JS, images. those files are not having session.
solution: Seems there is a way to avoid calling IHttpModule for all the request. ckeck this answer JS,Images and CSS getting intercepted by HTTPModule.
But it didn't work for me. So I uses HttpContext.Current.Session instead of HttpApplication.Session and every time it checks if Session is null before save to session.
If some one knows, How to avoid calling IHttpModule for files other than .aspx Please help here.
Here is the final code
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
if(HttpContext.Current.Session != null){
HttpContext.Current.Session["language"] = "test";
}
}
}
Edit: One other way is only use session if request comes to a .aspx file
HttpApplication http_application = sender as HttpApplication;
HttpContext context = http_application.Context;
if(Path.GetExtension(context.Request.PhysicalPath) == ".aspx")
{
HttpContext.Current.Session["language"] = "test";
http_application.Session["language2"] = "test2";
}

Related

SharePoint 2013 - redirection when session ends

I created Global.asax file and put it into my web application, which works on SharePoint 2013.
In Global.asax.cs file, I inherited SPHttpApplication class and override
BeginRequest event. It does not work, the Global.asax file is ignored by SharePoint, or I miss something else to add.
The goal is to create redirection when session expires. What am I doing wrong?
This is my code in code behind:
namespace PhoenixHR.Global
{
public class PhenixHRHttpApplication : SPHttpApplication
{
public override void Init()
{
try
{
base.Init();
this.BeginRequest += new EventHandler(Moj_BeginRequest);
}
catch (Exception ex)
{
}
}
private void Moj_BeginRequest(object sender, EventArgs e)
{
try
{
if (HttpContext.Current.Session["TPL"] == null)
Response.Redirect("~/PhoenixHR/start.aspx");
}
catch (Exception ex)
{
}
}
}
}
Global.asax
<%# Assembly Name="Microsoft.SharePoint"%>
<%# Application Language="C#" Inherits="PhoenixHR.Global.PhenixHRHttpApplication,PhoenixHR.Global,Version=1.0.0.0,Culture=neutral,PublicKeyToken=a0bd8fabe3543dc0" %>
As you have some exception swallowing in your code can you prove the change is not working? or is it raising an exception every request and just ignoring it?
You will be better off with binding to a different event. The HttpApplication class has quite a few events you can bind and BeginRequest is very early (first) in the processing of the request pipeline. This will not have access to the Session State. I would suggest using AcquireRequestState or PostAcquireRequestState.
In order for this to work you will need to create an HttpModule.
namespace PhoenixHR.Global
{
public class SessionMissingRedirectHttpModule : IHttpModule
{
private const string LAYOUTS_SUBFOLDER = "/PhoenixHR.Global";
private const string START_PAGE = "/start.aspx";
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.PostAcquireRequestState += context_PostAcquireRequestState;
}
void context_PostAcquireRequestState(object sender, EventArgs e)
{
// get required contexts
SPHttpApplication application = (SPHttpApplication)sender;
HttpContext context = application.Context;
HttpSessionState session = context.Session;
if (session == null)
return;
if (session["TPL"] != null)
return;
// get SharePoint context and current SPWeb.
SPContext sharepointContext = SPContext.GetContext(context);
SPWeb currentWeb = sharepointContext.Web;
// build a url to the redirect page.
string layoutsFolder =
SPUtility.GetLayoutsFolder(currentWeb);
string url = string.Format("{0}/{1}{2}{3}", currentWeb.Url, layoutsFolder, LAYOUTS_SUBFOLDER, START_PAGE);
// prevent redirection loop
if (context.Request.Url.AbsoluteUri.Equals(url, StringComparison.InvariantCultureIgnoreCase))
return;
SPUtility.Redirect(url, SPRedirectFlags.Trusted, context);
}
}
}
To register the HttpModule you will need to edit the web.config of the Web Application that it will run in.
In the /Configuration/system.webServer/modules path add the following
<add name="SessionMissingRedirectHttpModule" type="PhoenixHR.Global.SessionMissingRedirectHttpModule, PhoenixHR.Global,Version=1.0.0.0,Culture=neutral,PublicKeyToken=a0bd8fabe3543dc0" />
There are some best guesses for paths to the required application pages so you may need to alter the variables to suit.

Flag for application started in a framework

I am trying to execute some code in the application start of an HTML Module. Since the Init() gets fired multiple times, is there a reliable flag to tell me if the application started or not?
public class Module : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
//PROCESS ON APPLICATION START EVENT
this.OnApplicationStart(context);
}
#endregion
public void OnApplicationStart(HttpApplication context)
{
if (!application started??) //FRAMEWORK FLAG?
//DO SOMETHING
}
}
You could use a flag:
public class Module : IHttpModule
{
private static bool isStarted = false;
private static object syncRoot = new object();
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
if (!isStarted)
{
lock (syncRoot)
{
if (!isStarted)
{
//PROCESS ON APPLICATION START EVENT
this.OnApplicationStart(context);
isStarted = true;
}
}
}
}
public void OnApplicationStart(HttpApplication context)
{
//DO SOMETHING
}
}
As a better alternative to using HttpModules to perform this task if you are targetting .NET 4.0 or later I would recommend you using WebActivator which is a very handy package based on Microsoft.Web.Infrastructure allowing you to subscribe to events such as Application_Start in separate libraries.
For example, simply put the following code in a class library:
[assembly: WebActivator.PreApplicationStartMethod(typeof(WebAppInitializer), "Start")]
namespace FooBar
{
public static class WebAppInitializer
{
public static void Start()
{
// PROCESS ON APPLICATION START EVENT
}
}
}
and then referencing the class library in your ASP.NET application is all it takes.
You could also use this handy WebActivator to perform dependency injection into your HttpModules and self register them without the need to add them to web.config. Phil Haack wrote a nice blog post on this topic if you are interested.

Call StaticFileHandler

I have an HttpHandler mapped to aspnet_isapi.dll to perform a custom authentication check on static files (.pdf files) using IIS 7.5 in Classic mode:
void IHttpHandler.ProcessRequest(HttpContext context)
{
if(!User.IsMember) {
Response.Redirect("~/Login.aspx?m=1");
}
else {
//serve static content
}
}
The above code works fine, except for the else statement logic. In the else statement, I simply want to allow the StaticFileHandler to process the request, but I haven't been able to sort this out. Any suggestions on how to simply "hand off" the file back to IIS to serve the request as a normal StaticFile request, would be appreciated.
To answer your question directly, you can create a StaticFileHandler and have it process the request:
// Serve static content:
Type type = typeof(HttpApplication).Assembly.GetType("System.Web.StaticFileHandler", true);
IHttpHandler handler = (IHttpHandler)Activator.CreateInstance(type, true);
handler.ProcessRequest(context);
But a better idea might be to create an HTTP module instead of an HTTP handler:
public class AuthenticationModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AuthorizeRequest += this.Application_AuthorizeRequest;
}
private void Application_AuthorizeRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
if (!User.IsMember)
context.Response.Redirect("~/Login.aspx?m=1");
}
}

How to handle Application_End event from outside global.asax

We can attach to this event from global.asax file creating method with name Application_End(). But I need to attach to it like this
HttpContext.ApplicationInstance.ApplicationEnd+=OnApplicationEnd;
Is there any way to do it?
Have solved the problem in such way.
public class MyHttpApplication:HttpApplication
{
public event Action ApplicationEnd;
protected void Application_End()
{
if (ApplicationEnd != null)
ApplicationEnd();
}
}
In global.asax defined
<%# Application Inherits="MyLib.MyHttpApplication" Language="C#" %>
Then in code
var app = HttpContext.ApplicationInstance as MyHttpApplication;
app.ApplicationEnd += () => { // do something };
Application_End is a special "event" that is called by Asp.net that doesn't belog to the HttpApplication class.
From MSDN
*The Application_Start and Application_End methods are special methods that do not represent HttpApplication events. ASP.NET calls them once for the lifetime of the application domain, not for each HttpApplication instance.*
I think you can have the same behaviour attaching and handler to the AppDomain.DomainUnload event
//your global.asax class contrauctor
public GlobalApplication()
{
AppDomain.CurrentDomain.DomainUnload += ApplicationEnd;
}
private void ApplicationEnd(object sender, EventArgs e)
{
}
I know the answer is already given but wanted to also include this way:
[assembly: PreApplicationStartMethod(typeof(Bootstraper), "Start")]
[assembly: ApplicationShutdownMethod(typeof(Bootstraper), "End")]
public static class Bootstraper
{
public static void End()
{
...
}
public static void Start()
{
...
}
}

HttpContext.Current.Session is null

I have a WebSite with a custom Cache object inside a class library. All of the projects are running .NET 3.5.
I would like to convert this class to use Session state instead of cache, in order to preserve state in a stateserver when my application recycles.
However this code throws an exception with "HttpContext.Current.Session is null" when I visit the methods from my Global.asax file. I call the class like this:
Customer customer = CustomerCache.Instance.GetCustomer(authTicket.UserData);
Why is the object allways null?
public class CustomerCache: System.Web.SessionState.IRequiresSessionState
{
private static CustomerCache m_instance;
private static Cache m_cache = HttpContext.Current.Cache;
private CustomerCache()
{
}
public static CustomerCache Instance
{
get
{
if ( m_instance == null )
m_instance = new CustomerCache();
return m_instance;
}
}
public void AddCustomer( string key, Customer customer )
{
HttpContext.Current.Session[key] = customer;
m_cache.Insert( key, customer, null, Cache.NoAbsoluteExpiration, new TimeSpan( 0, 20, 0 ), CacheItemPriority.NotRemovable, null );
}
public Customer GetCustomer( string key )
{
object test = HttpContext.Current.Session[ key ];
return m_cache[ key ] as Customer;
}
}
As you can see I've tried to add IRequiresSessionState to the class but that doesn't make a difference.
Cheers
Jens
It isn't really about including the State inside your class, but rather where you call it in your Global.asax. Session isn't available in all the methods.
A working example would be:
using System.Web.SessionState;
// ...
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
{
HttpContext context = HttpContext.Current;
// Your Methods
}
}
It will not work e.g. in Application_Start
Depending on what you're trying to do, you may also benefit from using Session_Start and Session_End in Global.asax:
http://msdn.microsoft.com/en-us/library/ms178473(v=vs.100).aspx
http://msdn.microsoft.com/en-us/library/ms178581(v=vs.100).aspx
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
}
Note the restrictions on SessionState Mode before relying on Session_End.

Categories

Resources