MVC Equivalent of Page_Load - c#

I have a session variable that is set in my MVC application. Whenever that session expires and the user tries to refresh the page that they're on, the page will throw an error because the session is not set anymore.
Is there anywhere I can check to see if the session is set before loading a view? Perhaps putting something inside the Global.asax file?
I could do something like this at the beginning of EVERY ActionResult.
public ActionResult ViewRecord()
{
if (MyClass.SessionName == null)
{
return View("Home");
}
else
{
//do something with the session variable
}
}
Is there any alternative to doing this?
What would the best practice be in this case?

If it's in one controller, you can do this:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
... // do your magic
}
It will fire before on any action execution. You can't return a view from there though, you'll have to redirect to anything that returns action result, e.g:
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Shared" }, { "action", "Home" } });
But, obviously, that should redirect to the action in the controller that's not affected by the override, otherwise you have a circular redirect. :)

First, you should redirect to Home, not return the Home View, otherwise you have the weird situation of the home page showing up despite the Url being somewhere else.
Second, Session will never be Null, because a new session gets created when the old one expires or is reset. You would instead check for your variable and if THAT is null, then you know the session is new.
Third, If you app depends on this session data, then I would not use a session at all. Are you using this to cache data? If so, then using Cache may be a better choice (your app gets notified when cache items expire).
Unfortunately, this is probably a case of The XY Problem. You have a problem, and you believe Session solves your problem, but you're running into a different problem with Session, so you are asking how to solve your session problem rather than how to solve the problem Session is trying to solve.
What is the real problem you are trying to solve with this?
EDIT:
Based on your comment below, why don't you pass the customer number on the url:
http://website/Controller/ViewRecord/3
public ActionResult ViewRecord(int id)
{
// do whatever you need to do with the customer ID
}

Related

MVC 3 How can I make a user view a warning/disclaimer screen

I thought this would be very simple but I'm struggling a little. I'm working on a project for a client using MVC 3 that requires users to agree to certain conditions before using the site. I have created a standard agree/disagree screen which is loaded when first coming into the site, but if a user types a address to a different part of the site they can bypass the conditions for example www.test.com loads the conditions but if the user types www.test.com/home they bypass the conditions.
How can I make sure they have agreed to the conditions before they can get anywhere else on the site? I have been trying a session variable, which I think is the way to go, but is there a way to check this variable on every page request without having to write a check into every Controller Action on the site?
You could make a custom attribute and add it to the top of the Controller.
For example:
[AgreedToDisclaimer]
public ActionResult LoadPage()
{
return View();
}
Which would only load the view if the AgreedToDisclaimer returns true.
public class AgreedToDisclaimerAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
// logic to check if they have agreed to disclaimer (cookie, session, database)
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Returns HTTP 401 by default - see HttpUnauthorizedResult.cs.
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "ActionName" },
{ "controller", "ControllerName" },
{ "parameterName", "parameterValue" }
});
}
}
http://msdn.microsoft.com/en-us/library/dd410209(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.handleunauthorizedrequest.aspx
There are two approaches for the issue:
If this condition alter the site (like StackOverflow's notifications, that show at the top), but do not prevent you from using it, then I think it should be solved in presentation logic ( thus in the view, if you have real view an not just glorified template ).
Session variable in this case is just another part of model layer's state. When view instance requests data from the model layer, it is informed, that there is new notification for the user. This would mean that each view would have an ability to add representation of this notification to the current response. Preferably, by choosing to use one additional template, when assembling the output. This functionality would be shared between all views and thus could be implemented in base class, providing one single point of change.
If this acceptance of disclaimer is mandatory for everyone (like "I am 18 or older" in adult-themed sites), then the best option, in this case, would be to check, if user has agreed to condition in the routing mechanism. I am not sure how much control you have over request routing in ASP.NET MVC, but that again would provide you with as single point of change.
If decision is made in routing mechanism, it would also mean, that you put the condition outside the standard MVC triad entirely.

Losing session data stored in one PartialViewResult when trying to retrieve it in another PartialViewResult

I have a scenario where in I am using the Session to store an object in one controller action and trying to retrieve it another controller action. Both the actions are triggered from the same view and reside on the same controller. Unfortunately, I am not able to retrieve the session variable in the second controller. The session Id remains the same and I am ensuring that the object was written into the session in the first action. The session data, however, disappears when the view is returned in the first action.
Here is my Controller code flow -
public PartialviewResult DoSearch(string paramCustId)
{
//invoking a method to perform a search task. I am also passing the controller session as a parameter
//this function is called in a separate thread and the main thread does not wait for it to complete before returning the view
multiSearch(paramCustId, Session);
}
return PartialView("_partialView1");
public void multiSearch(string searchParam, HttpSessionStateBase controllerSession)
{
//code to retrieve response from backend into the variable tempSearchSet
controllerSession["searchResult"] = tempSearchSet;
//verified that tempSearchSet is stored in Session under the key "searchResult" and Session.Count is 1.
}
//Another controller action that is triggered from the same view after a certain delay to fetch the data in session
public PartialViewResult PollSearchResults()
{
var tempSearchResult = Session["searchResult"] as List<SearchResultSet>;
//This is where i do not see data in the session. I have verified that the multiSearch method is complete and has updated the data in the session.
//here Session.SessionID is the same as above, but Session.Count is 0
}
Is there a different way to handle Session in mvc or am i missing something elementary here? Also, is there a better approach to manage this caching scenario?
Found the solution. I had to initialize the session variable in the Session_Start method of Global.asax.cs. This one line made it work for me.
HttpContext.Current.Session.Add("searchResult", new List<SearchResultSet>());
I am still unclear why this line is needed as the session variable get and set worked in the first action method without the initialization. Guess i'll keep that for future research.

How can I redirect a site based on date in ASP.NET MVC?

I'm trying to find the best way to redirect a user to a page based on the current date. Exactly What I'm trying to accomplish is in the code below.
DateTime Today = DateTime.Now;
DateTime LaunchDate = DateTime.Parse("17/06/11");
DateTime CloseDate = DateTime.Parse("19/06/11");
int isClosed = DateTime.Compare(CloseDate, Today);
int isOpen = DateTime.Compare(LaunchDate, Today);
if (isClosed < 0){
return RedirectToAction("Closed", "Home");
}
else if (isOpen > 0){
return RedirectToAction("Index", "Home");
}
else{
return RedirectToAction("ComingSoon", "Home");
}
Where in the global.asax(or is it even possible) would this condition go?
I would put that code in a custom MvcHandler.
You could put it in ActionFilter, but then you would have to apply it to all actions.
Here's the code I use for a similar requirement, with a couple of additional features to make testing easier. It could be set up as a global filter, though I prefer to apply it to controllers/actions individually so that specific pages can be available before launch.
Note that this returns a ViewResult rather than a RedirectResult - this way the original URL is maintained, which means that if someone with the right role logs in from the placeholder page they can be redirected to the URL they originally requested.
public sealed class PreviewAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// todo: if site is live, show page
if (DataStore.Instance.Settings.Get("islive") == "True") return;
// if request is from localhost or build server, show page
if (filterContext.HttpContext.Request.IsLocal) return;
if (filterContext.HttpContext.Request.UserHostAddress.StartsWith("192.168.0")) return;
// if user has has beta role, show page
if (filterContext.HttpContext.Request.IsAuthenticated && (filterContext.HttpContext.User.IsInRole("Beta"))) return;
// site is not live and user does not have access - show placeholder
filterContext.Result = new ViewResult()
{
ViewName="Placeholder",
ViewData = filterContext.Controller.ViewData,
TempData = filterContext.Controller.TempData
};
}
}
I would not do this in global.asax, although you could set this up to figure out the routes you have registered. Assumes that the entire "site" will be coming soon, open and closed. The problem with this method, if it works in your case, is someone can circumvent by playing around. Ouch!
As I was typing, Jakub popped up with Handler, which is a good option. You can set it so no pages can be seen other than the one you desire, which is what it sounds like you want.
You could create custom RouteConstraints by implementing the IRouteConstraint. Within the match method you could add the logic for the datetimes you want to check. This would require you to have multiple routes in your routetable. which all refer to different controllers/actions where you can show a different View to the user.
Below url shows lots of samples on how to implement a custom RouteConstraint.
http://stephenwalther.com/blog/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints.aspx

How to set a default initialization cookie in ASP.NET MVC

I was wondering if anyone can shed some light on cookie management. More precisely I would like to know how I could set up an initial cookie/s when a user start a session in a website.
Currently the ASP.NET_SessionId cookie is located on user computers when they navigate to the domain. I would like to set up an additional cookie with details of languageid and countryid with default parameters the first time the user navigate to the site.
Do anyone knows if there is any technique to do this, such as through web.config set up or placing cookie details using layout.cshtml like
Response.Cookies["language"].Value = "1";
Response.Cookies["country"].Value= "7";
or similar?, any option will be appreciated.
You could do this in an action filter:
public class LocalizationAwareAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var httpContext = filterContext.HttpContext.Current;
if (!httpContext.Cookies.Keys.Contains("language"))
{
httpContext.Response.AppendCookie(new HttpCookie("language", 1));
}
if (!httpContext.Cookies.Keys.Contains("country"))
{
httpContext.Response.AppendCookie(new HttpCookie("country", 7));
}
}
}
The filter can be applied globally, so you don't have to worry about remembering it on each action method or controller.
I haven't worked much with cookies in ASP.NET MVC, but the way I'd do it would be to have a little block of code in Global.asax or a separate base controller that gets executed on every request. This code would check the HttpContext for the existence of such a cookie, and if it didn't exist, it would create one. I'll work up a code sample and will update this answer soon.

C# asp.net MVC: When to update LastActivityDate?

I'm using ASP.NET MVC and creating a public website. I need to keep track of users that are online. I see that the standard way in asp.net of doing this is to keep track of LastActivityDate. My question is when should I update this?
If I update it every time the users clicks somewhere, I will feel a performance draw back. However if I do not do that, people that only surf around will be listed as offline.
What is the best way to do this in asp.net MVC?
Just put an ajax javascript call at the bottom of your master page to track this.
Don't worry about performance at this time. If it's implemented and you see it being a problem then come back to finding a better solution. Something so simple shouldn't be a performance problem.
Just think about it like Google analytics. It sits at the bottom of millions of pages with little to no impact on the user experiences of those sites.
Just ran into the same problem, here's my answer for MVC users:
The idea is to trigger Membership.GetUser("..", true) for every page load. This will automatically update the LastActivityDate.
I put this in my global.asax under "RegisterGlobalFilters":
filters.Add(new MembershipTriggerLastActivityDate());
I created a new class that looks like this:
class MembershipTriggerLastActivityDate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
MembershipUser user = Membership.GetUser(filterContext.HttpContext.User.Identity.Name, true);
}
base.OnActionExecuting(filterContext);
}
}
I started using the SimpleMembershipProvider. It's so simple that there's no more LastActivityDate tracking. So I had to roll my own.
I just added a LastActivityDate column in the Users table and was good to go...
Following #Jab's tip and using the _Layout.cshtml page (master page) in an ASP.NET MVC app, I did this with the help of jQuery:
$(document).ready((function () {
var isUserAuthenticated = '#User.Identity.IsAuthenticated';
if (isUserAuthenticated) {
$.ajax({
type: "POST",
url: "#Url.Action(MVC.Account.ActionNames.UpdateLastActivityDate, MVC.Account.Name)",
data: { userName: '#User.Identity.Name' },
cache: false
});
}
});
Here's the action method:
public virtual ActionResult UpdateLastActivityDate(string userName)
{
User user = Database.Users.Single(u => u.UserName == userName);
user.LastActivityDate = DateTime.Now;
Database.Entry(user).State = EntityState.Modified;
Database.SaveChanges();
return new EmptyResult();
}
Only 133ms (YMMV) :-)
Why not implement the update to LastActivityDate as an asynchronous call? That way you can fire the update and continue processing.
As #Jab says, just implement it and if you see it as a performance issue in the future - deal with it then.
This is how I've done it in my application:
protected void Application_EndRequest()
{
if ((Response.ContentType == "text/html") && (Request.IsAuthenticated))
{
var webUser = Context.User as WebUser;
if (webUser != null)
{
//Update their last activity
webUser.LastActivity = DateTime.UtcNow;
//Update their page hit counter
webUser.ActivityCounter += 1;
//Save them
var webUserRepo = Kernel.Get<IWebUserRepository>(); //Ninject
webUserRepo.Update(webUser);
}
}
}
I haven't had any problems with performance.
HTHs,
Charles
I put it into a special queue that allows only one of a given key to be in the queue (and use the userId as the key in this case). Then I have a low priority thread that works its way through this queue doing database updates. Thus no slow down for the user, and one user doing 100 updates in one second doesn't cause any harm. If it ever becomes an issue I'll make those updates into batch updates against the database but for now this approach works just fine.
If the application crashed I'd lose a few seconds of last activity data but that's just fine. Of course I also update the in memory User object every time so that it's reflected in the UI even if it hasn't made its way to the database yet. Typically it's there before they've received the completed page anyway.
If you are using InProc SessionState, use the SessionStateModule.End event. This happens when the session state is evicted from the underlying cache storage. Typically this happens after 20 minutes of inactivity, you can set the time in web.config.
Good question, have thought about that too and how accurate these mechanisms can be considering performance, a couple of ideas:
1) Track the the last login date
2) Use the LastLoginDate + the expected session length to set some kind of LastOnlineDate that can be used to check if the user is online.
I don't think there is big penalty in performance if you fetch the current logged-in user on every request and update the LastActivityDate field every time (if you have care and invoke the GetUser method for the logged-in user once per http-request). In this way you can also make sure you always have the user's data fresh, like email, name, etc in case he/she updates that data.
I tried Charlino's code in the Global.asax like this
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Response.ContentType == "text/html") && (Request.IsAuthenticated))
{
}
}
However I was getting the Request.IsAuthenticated false all the time.
So I moved the code to a method in my Site.Master pagelike this
public void RegisterActivity()
{
if ((Response.ContentType == "text/html") && (Request.IsAuthenticated))
{
string userName = Page.User.Identity.Name;
UserManager userManager = new UserManager();
AppUser appUser = userManager.FindByName(userName);
appUser.LastActivityDate = DateTime.UtcNow;
userManager.Update(appUser);
}
}
I call the method from the Master page Page_Load event, and there it worked.
I'm using asp.net Identity not Membership but I added an AppUser class inheriting from the IdentityUser class and in the AppUser class I added LastActivityDate property.
This is in a WebForms Applicaction not MVC.

Categories