views:

143

answers:

3

I could see, myself and many people are having trouble with this two items in ASP.NET... Refresh Button, Back Button... (I see many people went to the exent of asking how to disable these two buttons in the browser..)

Now what are the problems in Implementing two more boolean variables in Page (IsRefresh, IsPostBack)... If those problems can be circumvened and implemented, it would be a great asset to developers...

When you are answering, if you could also include the steps you are taking in your web app in order to avoid reposting (at times in DB) in such scenarios that would be helpful.

Thanks

+1  A: 

It's not quite that clear cut. The only way the server can identify a "PostBack" is based on the headers passed to the page by your request. When you POST a form, "IsPostBack" is true, as you would expect. When you refresh a page or press Back the exact same request data is sent. There's no way for the application to detect that the user issued a non-standard request (Back or Refresh) without changing the behavior of the browser that sends the request.

As you know, most browsers will indicate to you that "Clicking Back will resend the form data...", but that is merely a warning issued by the browser to let you know that it is going to send the exact same request over again. The server does not know this and has no (native) way to interpret the information.

Double Postback Prevention Tips

One way to prevent data getting posted twice is to ensure that each PostBack contains some unique data that you can validate. The process is fairly simple.

On each page load you will want to create a unique identifier for that page's PostBack events. It doesn't matter what the unique id is, so long as it isn't the same on sequential page loads. Place that unique identifier in a hidden field on your page AND in the user's session (or a cookie). Then, on each PostBack validate that the cookie in the hidden field matches the value in the session. If the values match the PostBack is an original post to the page which can be processed accordingly. After performing the necessary operations you will then need to change the unique identifier in both locations again. This way, if the user should hit back and choose to "Resend Data", the hidden field will not match the session key and you can throw out the duplicate post data.

Nathan Taylor
I would like (read as desperate) to know, what steps should I take, in such scenarios, in order to avoid another call/command execution in the DB... A classic example would be a page where user enters comments... Hitting refresh insert the comment again, and it would be hard to check whether the same comment is already entered as the comment is long and there is a possibility that the same comment could have validly got entered.
The King
@The King: It's easy. After adding the comment to the database you issue a Response.Redirect to the same page. Then when the user refreshes or goes back no comment will be added again.
klausbyskov
Or that works too. Simple and elegant.
Nathan Taylor
+4  A: 

The problem with implement the two additional boolean properties is, that there really is no (reliable) way to distinguish Back / Refresh triggered requests. When hitting on of these buttons, the browser will either:

  1. display the page from the cache if allowed, or
  2. execute the exact same request again (possibly asking the user to confirm first)

You are experiencing case #2. When the second request occurs, the server will recieve the exact same request data as it did with the original request. Thus, ASP.NET (and most other frameworks) will handle the request just like the original was handled. There are several ways to work around this problem:

  1. Change your caching policy to allow the browser to redisplay the results of previous requests (might solve the Back problem, but not Refresh).
  2. Add a hidden field (or data in the ViewState) containing a unique value on the page from where postbacks are expected. Then add some data structure to keep a list of "used" values, along with logic to test for duplicates before doing anything critical.
  3. Use the Post/Redirect/Get pattern to ensure that the POST request never ends up in the browser history.

Solution #3 is dead easy, and works in almost every case. Basically, rather then returning the results/confirmation as a reply to the "critical" postback, do a redirect to a new URL:

protected void btnCritical_Click(object sender, EventArgs e)
{
    DoSomethingThatShouldNotBeDoneTwice();
    Response.Redirect("confirmation.aspx");
}
Jørn Schou-Rode
+1  A: 

aspnet_isapi recognizes a postback from the form content, specifically the ViewState field. It has no intrinsic way to distinguish a postback from a refresh.

i have seen a few strategies for working with this in the past. One is to assign each request a guid and use that as a unique key or index in the table you are writing to.

You can create a base page that all of your pages inherit from.

public partial class PageBase : System.Web.UI.Page
{
    private Guid _requestId;

    protected Guid RequestId
    {
        get
        {
            return _requestId;
        }   
    }
    protected virtual void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            _requestId = Guid.NewGuid();
            ViewState.Add("requestId", _requestId);
        }
        else
        {
            _requestId = (Guid)ViewState["requestId"];
        }
    }
}

public partial class _Default : PageBase
{
    protected override void Page_Load(object sender, EventArgs e)
    {
        base.Page_Load(sender, e);

        // now do stuff
    }
}
Sky Sanders
yes, you could use this to implement jorns suggestion of keeping track of 'used' requests.
Sky Sanders