2

I understand that are similar question on StackOverflow about this problem, but none of it solved my issue, so i'm creating a new one.

As the title says, i'd like to detect when an user refreshs a page. I have a page where i save some log information about what the user done on it (things like adding, removing or edting items). This log can only be saved when an user leaves the page, not by refreshing it.

I tried the example below to detect if it's a refresh or a new request:

public ActionResult Index()
{
   var Model = new Database().GetLogInfo();
   var state = TempData["refresh"];

   if(state == null)
   {
    //This is a mock structure
    Model.SaveLog(params);
   }


TempData["refresh"] = true; //it can be anything here

return View();
}

Considering it's a TempData it should expire on my next action. However, it's surviving the entire application for some reason. According to this blog it should expire on my subsequent request (unless i'm not understandig something). Even if i log out from my app and log in again, my TempData is still alive.

I've been thinking about use the javascript function onbeforeunload to make an AJAX call to some action, but once again i'd have to rely on TempData or to persist this refresh info somehow. Any tips?

AdrianoRR
  • 1,131
  • 1
  • 17
  • 36
  • what version of asp.net mvc are you using? – user182630 Nov 23 '11 at 15:10
  • 2
    This seems like a difficult way to go about this. Since "doing anything" involves invoking an action on a controller, why not just log that activity when it's done rather than when the user leaves a page? I do similar auditing, but I've embedded it into my data context by overriding the `SaveChanges` method to push all changes to the respective audit tables of each entity. – tvanfosson Nov 23 '11 at 15:13
  • @user182630 I'm using Asp.Net MVC3 – AdrianoRR Nov 23 '11 at 15:21
  • @tvanfosson One of the thing i have to record is how much time an user spends on this page. And i can only record that once the user leaves it or changes it, not by refreshing it (considering he's still on the same page). – AdrianoRR Nov 23 '11 at 15:23
  • @AdrianoRR a beforeunload handler that does a fire and forget action on the server to record the current timestamp seems reasonable, but what are you going to do to handle the case where the user closes their laptop with the page open? I'm thinking this isn't a particularly reliable number anyway and it might be good enough just to record a timestamp each time a user hits a (non-AJAX) action, then calculate the "time per view" from those timestamps. – tvanfosson Nov 23 '11 at 15:35
  • @tvanfosson I understand your concern, but we already tested it (not on a laptop unfortunately) and yes, that timestamp is not completely reliable. Luckily, we made a function that evaluates the incorrect time by comparing with the server time. Now it works flawlessly. But thanks for your tips. Is always nice to hear people with more experience. – AdrianoRR Nov 23 '11 at 16:07

2 Answers2

8

You can use a ActionFilter that looks something like this:

public class RefreshDetectFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var cookie = filterContext.HttpContext.Request.Cookies["RefreshFilter"];
        filterContext.RouteData.Values["IsRefreshed"] = cookie != null &&
                                                        cookie.Value == filterContext.HttpContext.Request.Url.ToString();
    }
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        filterContext.HttpContext.Response.SetCookie(new HttpCookie("RefreshFilter", filterContext.HttpContext.Request.Url.ToString()));
    }
}

Register it in global.asax. Then you can just do this in your controller:

if (RouteData.Values["IsRefreshed"] == true)
{
    // page has been refreshed.
}

You might want to improve the detection to also check the HTTP Method used (as a POST and GET url can look the same). Do note that it uses a cookie for the detection.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • It does seem to be a good solution, but would you register it on every controller? Would that be wise, considering that i only need this behavior on one particular page? Other than that, i tried the example above and `RouteData.Values["IsRefreshed"]` is always false. – AdrianoRR Nov 23 '11 at 17:15
  • 3
    Ok, i've just add a `ActionFilterAttribute` to your `RefreshDetectFilter` and it worked like a charm! Thanks. – AdrianoRR Nov 24 '11 at 12:42
  • 1
    The cookie way seems not to work very well for me, when I navigate to another page then come back, still is refreshed – zquanghoangz Aug 02 '19 at 05:02
2

If you are using MVC 2 or 3 then the TempData does not expire on the subsequent request, but on the next read.

http://robertcorvus.com/warning-mvc-nets-tempdata-now-persists-across-screens/

lalibi
  • 3,057
  • 3
  • 33
  • 41