handling sessions on every page of website - asp.net

I have a data driven website and the current users Id gets stored in Session["UserId"].So all the data that shows up in almost all the pages is user specific.and when a user is using the site anonymously,it is a different set of results that i show and has nothing to do with the UserId.
My problem is I have to check if the Session["UserId"] is not null at every line where I am using Session["UserId"] and i somehow feel that it is not the right way to do it.
Is there a way where I can check if the Session is not null on page_load? If my session turns out to be null, how do i handle it? the page won't even load at all.
I hope i was able to explain

Instead of check session on every of your pages, put the session control in a base class and make all your pages extends this class. Every time your page inits the Page_Init base method will check if user is authenticated. If it's not authenticated the method will throw an exception that will be catched by Page_Error method. This method will clear session resources and redirect to Default page.
Make a hyerarchical classes for session control:
public class UserSession { }
public class AnonymousSession : UserSession {}
On your Page Logon put the UserId on the session based on logon type:
bool isAnon = GetAnonymous(); // Check form page if login is anonymously
UserSession user;
if(isAnon)
user = new AnonymousSession();
else
user = new UserSession();
Session.Contents.Add("UserId", user);
Set a property in PageBase named Anonymously that tells you if user has entered anonymously, and use it in your pages to set the set results of each of your pages:
public class PageBase: System.Web.Ui.Page
{
// Check here if session type is anonymous
protected bool Anonymously
{
get
{
return (UserSession)Session.Contents["UserId"] is AnonymousSession;
}
}
protected void Page_Init(object Sender,System.EventArgs e)
{
var user = (UserSession)Session.Contents["UserId"];
if (user == null)
{
throw new SessionException();
}
}
protected void Page_Error(object sender, System.EventArgs e)
{
Exception ex = Server.GetLastError();
Server.ClearError();
if(ex is SessionException)
{
Context.Session.Clear();
Context.Session.Abandon();
FormsAuthentication.SignOut();
Server.Transfer("Default.aspx", true);
}
}
}

Related

Session created after the postback occured and page-load event doesnt use session correctly in ASP.NET

I’ve made a handy “user control” for login to my website and it is placed in the site master.
The natural procedure is that the user logs in the web site and he should be announced with a welcome message containing its full name. The full-Name naturally should sits in a session variable created when the user logged on.
There is no doubt that we place the desired code in the “page_load” event and expect it to pass user’s full-name to the right circumstances (here its a label named lblFullName) in order to print/show the welcome message when login button clicked, But the full-name doesn’t passed until the user logs in the website again (for the 2nd times).
Why this problem happens?
Its some part of my code:
protected void Page_Load(object sender, EventArgs e)
{
if (Session["FullName"]==null)
{
//nothing 2 do.
}
else
{
lblFullName.Text = Session["FullName"].ToString();
}
}
You probably set the Session variable in the user control after the Page_Load event has been processed, so that it will not see the user name until the next postback.
In order to update the Label text as soon as the login is validated, you could:
Define an event in the user control
Register an event handler in the main page
Call the event handler as soon as the user has logged in
In the event handler, update the Label text
You could also eliminate the Session variable by passing the user full name in an EventArgs derived class. If you make the following class accessible in the user control and in the main form:
public class StringEventArgs : EventArgs
{
public string Value { get; set; }
public StringEventArgs(string value)
{
Value = value;
}
}
then you can define this event in the user control:
public event EventHandler<StringEventArgs> UserLoggedIn;
In the function where the login is confirmed, you call the event handlers:
private void UserLoginValidation()
{
// Login validation is done here
bool loginSuccessful = ...
if (loginSuccessful && UserLoggedIn != null)
{
UserLoggedIn(this, new StringEventArgs(fullName));
}
}
In the main page, you register the event handler, which updates the Label:
protected void Page_Load(object sender, EventArgs e)
{
loginUserControl1.UserLoggedIn += loginUserControl1_UserLoggedIn;
...
}
private void loginUserControl1_UserLoggedIn(object sender, StringEventArgs e)
{
lblFullName.Text = e.Value;
}

Load different master pages for different users

In my Web-project (ASP.NET) I need 2 different master pages. One for users like "Admin", and one for the usual users. Where can I indicate what master page to load? How can I load the correct master page, depending on the user?
When your admin user try to log in check the username and password with the database and if the login credentials are valid, Set a session variable to indicate this is an admin session. Then you can have a method which returns true of false by checking the session value to tell you whether the current user is an admin or normal user.
When admin login is successfull, set this session variable
Session["adminUserName"]=txtUserName.Text;
Then write a method to check whether the current user is an admin or not
public bool IsAdmin()
{
if(Session["adminUserName"]!=null)
{
return true;
}
else
{
return false;
}
}
Have this method in a common place (like your base class or so ) and check during the page life cycle and load the appropriate master page.
void BasePage_PreInit(object sender, EventArgs e)
{
if(IsAdmin())
{
MasterPageFile = "~/MasterAdmin.master";
}
else
{
MasterPageFile = "~/MasterNormal.master";
}
}
If Its an ASP.NET MVC application, You can check this in your ActionMethod.
public ActionResult Index()
{
if(IsAdmin())
{
return View("Index", "MasterAdmin");
}
else
{
return View("Index", "MasterNormal");
}
}
Handle the Page_PreInit event in code-behind and set the MasterPageFile property to your liking.

ASP.Net MVC 3 Strange Session Behaviour

I have an mvc 3 app for which I'm implementing authorization using my own login view which checks if the users name and password are allowed and then sets a variable in the session to say that the user is loggged in. This kind of works but for one particular view it is behaving in a strange undesirable way. The said view contains a form which I use to input some data and upload a file. For some reason which I can't figure out, after this form is posted a new session is started and therefore the variable which remembered that the user was logged in is reset to false and subsequently the login page is displayed again.
I'm lost as to why the application is starting a new session at this point? I have not instructed it to do this. Can anyone recommend solutions to stop this behaviour and get it to keep the old session?
Thanks.
UPDATE - Some Code:
Note the session seems to be terminated immediately after the response to the posted Create form
CMS controller which uses a custom Autorize attribute called "RDAutorize" on all actions:
[RDAuthorize]
public class PhotoCMSController : Controller
{
public ActionResult Create()
{
/* Code omitted: set up a newPhoto object with default state */
/* Display view containing form to upload photo and set title etc. */
return View("../Views/PhotoCMS/Create", newPhoto);
}
[HttpPost]
public ContentResult Upload(int pPhotoId)
{
/* Code ommited: receive and store image file which was posted
via an iframe on the Create view */
string thumbnail = "<img src='/path/to/thumb.jpg' />";
return Content(thumbnail);
}
[HttpPost]
public ActionResult Create(string pPhotoTitle, string pCaption etc...)
{
/*Code omitted: receive the rest of the photo data and save
it along with a reference to the image file which was uploaded
previously via the Upload action above.*/
/* Display view showing list of all photo records created */
return View("../Views/PhotoCMS/Index", qAllPhotos.ToList<Photo>());
/* **Note: after this view is returned the Session_End() method fires in
the Global.asax.cs file i.e. this seems to be where the session is
being lost** */
}
}/*End of CMS Controller*/
Custom Authorize action filter:
public class RDAuthorize : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Boolean authorized = Convert.ToBoolean(
HttpContext.Current.Session["UserIsAuthorized"]
);
if (!authorized) {
/* Not logged in so send user to the login page */
filterContext.HttpContext.Response.Redirect("/Login/Login");
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext) {}
public override void OnResultExecuting(ResultExecutingContext filterContext) {}
public override void OnResultExecuted(ResultExecutedContext filterContext) {}
}/*End of Authorize Action Filter*/
Login controller:
public class LoginController : Controller
{
private PhotoDBContext _db = new PhotoDBContext();
public ActionResult Login()
{
string viewName = "";
Boolean authorized = Convert.ToBoolean(Session["UserIsAuthorized"]);
if (authorized)
{
viewName = "../Views/Index";
}
else
{
viewName = "../Views/Login/Login";
}
return View(viewName);
}
[HttpPost]
public ActionResult Login(string pUsername, string pPassword)
{
string viewName = "";
List<Photo> model = new List<Photo>();
var qUsers = from u in _db.Users
select u;
foreach (User user in qUsers.ToList<User>())
{
/* If authorized goto CMS pages */
if (pUsername == user.Username && pPassword == user.Password)
{
Session["UserIsAuthorized"] = true;
var qPhotos = from p in _db.Photos
where p.IsNew == false
select p;
model = qPhotos.ToList<Photo>();
viewName = "../Views/PhotoCMS/Index";
break;
}
}
return View(viewName, model);
}
}/* End of Login controller */
Turns out the whole ASP.Net application was restarting because as part of the photo upload I was storing the image file in a temporary folder and then deleting the directory after moving the file to a permanent location. Apparently its default behaviour for ASP.Net to restart if a directory within the web site is deleted. I found this post
which describes the problem and offers a solution whereby the following code is added to the Global.asax.cs file. Implementing this solution has fixed the problem. The fix is applied by calling FixAppDomainRestartWhenTouchingFiles() from the Application_Start() event:
protected void Application_Start()
{
FixAppDomainRestartWhenTouchingFiles();
}
private void FixAppDomainRestartWhenTouchingFiles()
{
if (GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
{
/*
From: http://www.aaronblake.co.uk/blog/2009/09/28/bug-fix-application-restarts-on-directory-delete-in-asp-net/
FIX disable AppDomain restart when deleting subdirectory
This code will turn off monitoring from the root website directory.
Monitoring of Bin, App_Themes and other folders will still be
operational, so updated DLLs will still auto deploy.
*/
PropertyInfo p = typeof(HttpRuntime).GetProperty(
"FileChangesMonitor", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
object o = p.GetValue(null, null);
FieldInfo f = o.GetType().GetField(
"_dirMonSubdirs", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase);
object monitor = f.GetValue(o);
MethodInfo m = monitor.GetType().GetMethod(
"StopMonitoring", BindingFlags.Instance | BindingFlags.NonPublic);
m.Invoke(monitor, new object[] { });
}
}
private AspNetHostingPermissionLevel GetCurrentTrustLevel()
{
foreach (AspNetHostingPermissionLevel trustLevel in
new AspNetHostingPermissionLevel[] {
AspNetHostingPermissionLevel.Unrestricted,
AspNetHostingPermissionLevel.High,
AspNetHostingPermissionLevel.Medium,
AspNetHostingPermissionLevel.Low,
AspNetHostingPermissionLevel.Minimal }
)
{
try
{
new AspNetHostingPermission(trustLevel).Demand();
}
catch (System.Security.SecurityException)
{
continue;
}
return trustLevel;
}
return AspNetHostingPermissionLevel.None;
}
Since sessions are associated with cookies, they are available for a specific domain.
It's a common mistake to ask for a session variable in the same application while the domain has changed (i.e. redirecting to a subdomain).
Does the controller action that you are posting the form contains any [Authorize] attribute. You need to post some code.
Verify a new session is really started every time. Check Trace output for the user's session id to ensure it realllly has changed.
Ensure the cookie getting sent over is actually getting set and sent over. (called ASPsessionIDSOMETHING ) and if that is being sent by the browser. Download the tool Fiddler to check the cookies easily (set cookie header coming from the server and the request cookies going back to the server from the browser. Make sure your browser is accepting the cookie and you dont say... have cookies turned off.
If your session id is changing at every request then your session isn't properly getting set the first time, set a break point on that code if you havent already.
You can log when the worker process resets - ensure that isn't the case. see http://www.microsoft.com/technet/prodtechnol/windowsserver2003/library/IIS/87892589-4eda-4003-b4ac-3879eac4bf48.mspx
I had the same problem. The problem only occured when a post request was send to the server and the session was not modified during that request. What I did as a workaround was, to write a custom filter which does nothing more than writing a key / value into the session on each request and added that filter to the GlobalFilter collection in the global.asax.
public class KeepSessionAlive : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
filterContext.HttpContext.Session["HeartBeat"] = DateTime.Now.ToShortDateString();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext) { }
}
And in the global.asax:
protected override void AddCustomGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new KeepSessionAlive());
}
This might be not the best solution but it helped me in my case.

ASP.NET Push Redirect on Session Timeout

I'm looking for a tutorial, blog entry, or some help on the technique behind websites that automatically push users (ie without a postback) when the session expires. Any help is appreciated
Usually, you set the session timeout, and you can additionally add a page header to automatically redirect the current page to a page where you clear the session right before the session timeout.
From http://aspalliance.com/1621_Implementing_a_Session_Timeout_Page_in_ASPNET.2
namespace SessionExpirePage
{
public partial class Secure : System.Web.UI.MasterPage
{
public int SessionLengthMinutes
{
get { return Session.Timeout; }
}
public string SessionExpireDestinationUrl
{
get { return "/SessionExpired.aspx"; }
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
this.PageHead.Controls.Add(new LiteralControl(
String.Format("<meta http-equiv='refresh' content='{0};url={1}'>",
SessionLengthMinutes*60, SessionExpireDestinationUrl)));
}
}
}
The SessionExpireDestinationUrl should link to a page where you clear the session and any other user data.
When the refresh header expires, it will automatically redirect them to that page.
You can't really "push" a client from your website. Your site will respond to requests from the client, but that's really it.
What this means is that you need to write something client-side (Javascript) that will determine when the user has timed out, probably by comparing the current time with the most recent time they have in a site cookie (which you update with the current time each time the user visits a page on your site), and then redirect if the difference is greater than a certain amount.
(I note that some people are advocating just creating a script that will forward the user after a certain amount of time on a page. This will work in the simple case, but if the user has two windows open on the site, and is using one window a lot, and the other window not-so-much, the not-so-much one will suddenly redirect the user to the forwarding page, even though the user has been on the site constantly. Additionally, it's not really in sync with any session keeping you're doing on the server side. On the other hand, it's certainly easier to code, and if that's good enough, then great!)
In the <HEAD> section, use a META refresh tag like this:
<meta http-equiv="refresh" content="0000; URL=target_page.html">
where 0000 is your session timeout in seconds, and target_page.html the address of the page to be redirected to.
Using Custom Page class and Javascript also we can achieve it.
Create a custom pagebase class and write the common functionality codes into this class. Through this class, we can share the common functions to other web pages. In this class we need inherit the System.Web.UI.Page class. Place the below code into Pagebase class
PageBase.cs
namespace AutoRedirect
{
public class PageBase : System.Web.UI.Page
{
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
AutoRedirect();
}
public void AutoRedirect()
{
int int_MilliSecondsTimeOut = (this.Session.Timeout * 60000);
string str_Script = #"
<script type='text/javascript'>
intervalset = window.setInterval('Redirect()'," +
int_MilliSecondsTimeOut.ToString() + #");
function Redirect()
{
window.location.href='/login.aspx';
}
</script>";
ClientScript.RegisterClientScriptBlock(this.GetType(), "Redirect", str_Script);
}
}
}
Above AutoRedirect function will be used to redirect the login page when session expires, by using javascript window.setInterval, This window.setInterval executes a javascript function repeatedly with specific time delay. Here we are configuring the time delay as session timeout value. Once it’s reached the session expiration time then automatically executes the Redirect function and control transfer to login page.
OriginalPage.aspx.cs
namespace appStore
{
public partial class OriginalPage: Basepage
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
OriginalPage.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeFile="OriginalPage.aspx.cs" Inherits="AutoRedirect.OriginalPage" %>
Web.config
<system.web>
<sessionState mode="InProc" timeout="3"></sessionState>
</system.web>
Note:
The advantage of using Javascript is you could show custom message in alert box before location.href which will make perfect sense to user.
In case if you don't want to use Javascript you could choose meta redirection also
public void AutoRedirect()
{
this.Header.Controls.Add(new LiteralControl(
String.Format("<meta http-equiv='refresh' content='{0};url={1}'>",
this.Session.Timeout * 60, "login.aspx")));
}
Just copy and paste this code snippet in your Web.Config file :
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" slidingExpiration="true" timeout="29" />
</authentication>
<sessionState timeout="30" mode="InProc" cookieless="false" />
You can put this line to your Site.Master :
Response.AppendHeader("Refresh",
Convert.ToString((Session.Timeout * 60)) +
";URL=~/Login.aspx");
I'm using MVC3 ASp.net as beginner, I tried many solution to solve my session problem ( since i'm using Session variable in my code, and after timeout i didn't have session values while i'm keep using it And I just find that my problem was in config file. the timeout between Authentication and sessionState should be so close. so they Killed (empty) at the same time // add timeout 1 and 2 for testing.. it's should be at least 29 and 30
I used others way it's work too :
Starting from :
protected void Session_Start(object src, EventArgs e)
{
if (Context.Session != null)
{
if (Context.Session.IsNewSession)//|| Context.Session.Count==0)
{
string sCookieHeader = Request.Headers["Cookie"];
if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
{
//if (Request.IsAuthenticated)
FormsAuthentication.SignOut();
Response.Redirect("/Account/LogOn");
}
}
}
}
protected 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.
Session.Clear();
}
And :
public class SessionExpireFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check if session is supported
if (ctx.Session != null)
{
// check if a new session id was generated
if (ctx.Session.IsNewSession)
{
// If it says it is a new session, but an existing cookie exists, then it must
// have timed out
string sessionCookie = ctx.Request.Headers["Cookie"];
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0))
{
ctx.Response.Redirect("~/Home/LogOn");
}
}
}
base.OnActionExecuting(filterContext);
}
}
And even worked with Ajax to solve session issuse:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Session.Count == 0 || Session["CouncilID"] == null)
Response.Redirect("/Account/LogOn");
if (Request.IsAjaxRequest() && (!Request.IsAuthenticated || User == null))
{
filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
}
else
{
base.OnActionExecuting(filterContext);
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.IsAjaxRequest())
{//validate http request.
if (!httpContext.Request.IsAuthenticated
|| httpContext.Session["User"] == null)
{
FormsAuthentication.SignOut();
httpContext.Response.Redirect("~/?returnurl=" + httpContext.Request.Url.ToString());
return false;
}
}
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
Data = new
{
// put whatever data you want which will be sent
// to the client
message = "sorry, but you were logged out"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Unfortunately it can't be done. The session timeout only occurs on the server side and you won't detect this until the user performs some kind of post back action.
However, what you CAN do is to inject some HTML or JavaScript header code that will automatically push the user to a logout page in the same timeframe as your session timeout. This doesn't guarantee a perfect synch, and you may run into issues if your user is doing some time intensive items and you are not resetting the clock.
I typically add this code to my Page_Load events to accomplish this.
' Register Javascript timeout event to redirect to the login page after inactivity
Page.ClientScript.RegisterStartupScript(Me.GetType, "TimeoutScript", _
"setTimeout(""top.location.href = 'Login.aspx'""," & _
ConfigurationManager.AppSettings("SessionTimeoutMilliseconds") & ");", True)
And if you use the following Logon controller, it will send you to the requested URL before logon:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
//return Redirect(returnUrl);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Of course you need to use [Authorize] over controller class or even Action in specific.
[Authorize]
public class MailController : Controller
{
}
Well this gets tricky for AJAX requests as Zhaph - Ben Duguid pointed out. Here was my solution to make this work with AJAX (using Telerik web controls but they are built using ASP.NET AJAX toolkit I believe).
In a nutshell, I rolled my own sliding expiration session type thing.
In my Site.Master, I am updating a session variable on EVERY postback (postback or AJAX request because AJAX requests still kick off the Page_Load event):
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
if (this.Request.IsAuthenticated)
this.pnlSessionKeepAlive.Visible = true;
else
this.pnlSessionKeepAlive.Visible = false;
}
if (this.Session["SessionStartDateTime"] != null)
this.Session["SessionStartDateTime"] = DateTime.Now;
else
this.Session.Add("SessionStartDateTime", DateTime.Now);
}
Then in my markup for my site.master, I included an iframe with a ASPX page I use "behind the scenes" to check and see if my custom sliding expiration has expired:
<asp:Panel runat="server" ID="pnlSessionKeepAlive" Visible="false">
<iframe id="frame1" runat="server" src="../SessionExpire.aspx" frameborder="0" width="0" height="0" / >
</asp:Panel>
Now in my SessionExpire.aspx page, I just refresh the page every so often and check if the timestamp has lapsed and if so, I redirect to my logout.aspx page that then determines which login page to send the user back to:
public partial class SessionExpire : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
/* We have to do all of this because we need to redirect to 2 different login pages. The default .NET
* implementation does not allow us to specify which page to redirect expired sessions, its a fixed value.
*/
if (this.Session["SessionStartDateTime"] != null)
{
DateTime StartTime = new DateTime();
bool IsValid = DateTime.TryParse(this.Session["SessionStartDateTime"].ToString(), out StartTime);
if (IsValid)
{
int MaxSessionTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["SessionKeepAliveMins"]);
IsValid = (DateTime.Now.Subtract(StartTime).TotalMinutes < MaxSessionTimeout);
}
// either their session expired or their sliding session timeout has expired. Now log them out and redirect to the correct
// login page.
if (!IsValid)
this.Logout();
}
else
this.Logout();
// check every 60 seconds to see if the session has expired yet.
Response.AddHeader("Refresh", Convert.ToString(60));
}
private void Logout()
{
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "TimeoutScript",
"setTimeout(\"top.location.href = '../Public/Logout.aspx'\",\"1000\");", true);
}
}
Many thanks to the people above who posted info, this lead me to my solution and hope it helps others.

Redirecting users from edit page back to calling page

I am working on a project management web application. The user has a variety of ways to display a list of tasks. When viewing a list page, they click on task and are redirected to the task edit page.
Since they are coming from a variety of ways, I am just curious as to the best way to redirect the user back to the calling page. I have some ideas, but would like to get other developers input.
Would you store the calling url in session? as a cookie? I like the concept of using an object handle the redirection.
I would store the referring URL using the ViewState. Storing this outside the scope of the page (i.e. in the Session state or cookie) may cause problems if more than one browser window is open.
The example below validates that the page was called internally (i.e. not requested directly) and bounces back to the referring page after the user submits their response.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Request.UrlReferrer == null)
{
//Handle the case where the page is requested directly
throw new Exception("This page has been called without a referring page");
}
if (!IsPostBack)
{
ReturnUrl = Request.UrlReferrer.PathAndQuery;
}
}
public string ReturnUrl
{
get { return ViewState["returnUrl"].ToString(); }
set { ViewState["returnUrl"] = value; }
}
protected void btn_Click(object sender, EventArgs e)
{
//Do what you need to do to save the page
//...
//Go back to calling page
Response.Redirect(ReturnUrl, true);
}
}
This message my be tagged asp.net but I think it is a platform independent issue that pains all new web developers as they seek a 'clean' way to do this.
I think the two options in achieving this are:
A param in the url
A url stored in the session
I don't like the url method, it is a bit messy, and you have to remember to include the param in every relevent URL.
I'd just use an object with static methods for this. The object would wrap around the session item you use to store redirect URLS.
The methods would probably be as follows (all public static):
setRedirectUrl(string URL)
doRedirect(string defaultURL)
setRedirectUrl would be called in any action that produces links / forms which need to redirect to a given url. So say you had a projects view action that generates a list of projects, each with tasks that can be performed on them (e.g. delete, edit) you would call RedirectClass.setRedirectUrl("/project/view-all") in the code for this action.
Then lets say the user clicks delete, they need to be redirected to the view page after a delete action, so in the delete action you would call RedirectClass.setRedirectUrl("/project/view-all"). This method would look to see if the redirect variable was set in the session. If so redirect to that URL. If not, redirect to the default url (the string passed to the setRedirectUrl method).
I agree with "rmbarnes.myopenid.com" regarding this issue as being platform independent.
I would store the calling page URL in the QueryString or in a hidden field (for example in ViewState for ASP.NET). If you will store it outside of the page scope (such as Session, global variable - Application State and so on) then it will not be just overkill as Tom said but it will bring you trouble.
What kind of trouble? Trouble if the user has more than one tab (window) of that browser open. The tabs (or windows) of the same browser will probably share the same session and the redirection will not be the one expected and all the user will feel is that it is a bug.
My 2 eurocents..
I personally would store the required redirection info in an object and handle globally. I would avoid using a QueryString param or the like since they could try bouncing themselves back to a page they are not supposed to (possible security issue?). You could then create a static method to handle the redirection object, which could read the information and act accordingly. This encapsulates your redirection process within one page.
Using an object also means you can later extend it if required (such as adding return messages and other info).
For example (this is a 2 minute rough guideline BTW!):
public partial class _Default : System.Web.UI.Page
{
void Redirect(string url, string messsage)
{
RedirectionParams paras = new RedirectionParams(url, messsage);
RedirectionHandler(paras); // pass to some global method (or this could BE the global method)
}
protected void Button1_Click(object sender, EventArgs e)
{
Redirect("mypage.aspx", "you have been redirected");
}
}
public class RedirectionParams
{
private string _url;
public string URL
{
get { return _url; }
set { _url = value; }
}
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
public RedirectionParams(string url, string message)
{
this.URL = url;
this.Message = message;
}
}

Resources