Prevent duplicate commit issues caused by F5 refreshes in ASP. NET Web form and MVC

Source: Internet
Author: User
Tags httpcontext

Reprint http://www.cnblogs.com/hiteddy/archive/2012/03/29/Prevent_Resubmit_When_Refresh_Reload_In_ASP_NET_Web_Form_MVC.html

What is refreshing/re-loading

Refresh in IE, called reload in FF and Chrome (RELOAD), differs from normal entry page in the following two points:

1. Cache control

If the file (compared to the slice) already exists in the local cache, the normal entry page will not access the server and load directly from the local. For a refresh operation, even if there is a local cache, the access server is forced to check for updates, with the "if-modified-since" label in the request header. If the file is not updated, the server returns 304, otherwise 200 and the updated file are returned.

2. Repeat the submission

The refresh will repeatedly submit the last request. Let's say you create a new order and save it, and the system succeeds and stays on the current page. At this point, the browser will pop up a dialog asking whether to continue, click OK after the browser will repeatedly submit the Last Post request (click on the saved request), the system may produce a duplicate order. Of course, if the last operation was just a GET request rather than a post, repeating commits does not cause this problem.

How to prevent duplicate submissions

The method of preventing duplicate submissions is somewhat similar to the PRG (Post-redirect-get) pattern, specifically, when the Web server recognizes a duplicate-committed POST request, redirects to the current page, and the browser requests the page in a GET way. If you use tools such as HttpWatch to view network requests, you can see that there will be two requests, 302 & 200.

The problem now is how to recognize that a request is caused by a refresh. We use the refresh operation to repeatedly submit the last request, not the characteristics of the current request, you can have the following scenarios: On the server side (Session) and page (can be a hidden field on the page) set an integer flag bit, in each POST request to compare the value of both, and then self-increment. In normal submission, the browser submits the value of the hidden field on the current page, so each time the value is equal to the session, and in the case of a refresh, the value submitted is the value of the last request, which is not equal to the session and is known to be a duplicate commit caused by the flush operation.

This is similar to token validation in struts.

Here we refer to the page hidden field called client Flag, the specific process is as follows

Hi teddy!

Teddy Li's Technical blog-life is like Try/catch, you hang up if you can't catch up

Prevent duplicate commits caused by F5 refreshes in ASP. NET Web form and MVC What is refresh/reload

Refresh in IE, called reload in FF and Chrome (RELOAD), differs from normal entry page in the following two points:

1. Cache control

If the file (compared to the slice) already exists in the local cache, the normal entry page will not access the server and load directly from the local. For a refresh operation, even if there is a local cache, the access server is forced to check for updates, with the "if-modified-since" label in the request header. If the file is not updated, the server returns 304, otherwise 200 and the updated file are returned.

2. Repeat the submission

The refresh will repeatedly submit the last request. Let's say you create a new order and save it, and the system succeeds and stays on the current page. At this point, the browser will pop up a dialog asking whether to continue, click OK after the browser will repeatedly submit the Last Post request (click on the saved request), the system may produce a duplicate order. Of course, if the last operation was just a GET request rather than a post, repeating commits does not cause this problem.

How to prevent duplicate submissions

The method of preventing duplicate submissions is somewhat similar to the PRG (Post-redirect-get) pattern, specifically, when the Web server recognizes a duplicate-committed POST request, redirects to the current page, and the browser requests the page in a GET way. If you use tools such as HttpWatch to view network requests, you can see that there will be two requests, 302 & 200.

The problem now is how to recognize that a request is caused by a refresh. We use the refresh operation to repeatedly submit the last request, not the characteristics of the current request, you can have the following scenarios: On the server side (Session) and page (can be a hidden field on the page) set an integer flag bit, in each POST request to compare the value of both, and then self-increment. In normal submission, the browser submits the value of the hidden field on the current page, so each time the value is equal to the session, and in the case of a refresh, the value submitted is the value of the last request, which is not equal to the session and is known to be a duplicate commit caused by the flush operation.

This is similar to token validation in struts.

Here we refer to the page hidden field called client Flag, the specific process is as follows

When you refresh the page in the last step, the value of the hidden field is 3, but the browser repeats the last request (2), which is inconsistent with the value in the session (3).

implementation in ASP. NET MVC

--------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------- --------------------------------------------

Add a custom Actionfilter.

  Public classNoresubmitattribute:actionfilterattribute {Private Static ReadOnly stringHttpmehotdpost ="POST"; Private Static ReadOnly stringprefix ="Postflag"; Private stringNamewithroute;  Public Override voidonactionexecuting (ActionExecutingContext filtercontext) {varControllerContext =FilterContext.Controller.ControllerContext; if(!controllercontext.ischildaction) {varRequest =controllerContext.HttpContext.Request; varSession =controllerContext.HttpContext.Session; Namewithroute=Generatenamewithroute (ControllerContext); intSessionflag = Session[namewithroute] = =NULL?0: (int) Session[namewithroute]; intRequestflag =string. IsNullOrEmpty (Request. Form[namewithroute])?0:int. Parse (Request.                Form[namewithroute]); //get or normal post:true;                 BOOLIsValid =! IsPost (filtercontext) | | Sessionflag = =Requestflag; if(Sessionflag = =int. MaxValue) {Sessionflag= -1; } Session[namewithroute]= ++Sessionflag; if(!isValid) {Filtercontext.result=NewRedirectresult (Generateurlwithtimestamp) (Request.                    RAWURL)); return; }            }            Base.        OnActionExecuting (Filtercontext); }        Private stringGenerateurlwithtimestamp (stringURL) {            return string. Format ("{0}{1}timestamp={2}", URL, url. Contains ("?") ?"&":"?", (Datetime.now-datetime.parse ("2010/01/01")).        Ticks); }        Private BOOLIsPost (ActionExecutingContext filtercontext) {returnFilterContext.HttpContext.Request.HttpMethod = =Httpmehotdpost; }        Private stringGeneratenamewithroute (ControllerContext controllercontext) {StringBuilder sb=NewStringBuilder (prefix); foreach(ObjectRouteValueinchcontrollerContext.RouteData.Values.Values) {sb. AppendFormat ("_{0}", RouteValue); }            returnsb.        ToString (); }         Public Override voidonresultexecuted (ResultExecutedContext filtercontext) {Base.            Onresultexecuted (Filtercontext); if(!filtercontext.ischildaction &&!) (Filtercontext.result isRedirectresult)) {                //string format = "<script type= ' text/javascript ' >$ (function () [[$ (' form ')]. each (function () [[$ (' <input Type=hidden id={0} name={0} value={1}/> '). AppendTo ($ (this));]]); </script> ";                stringFormat ="<script type= ' Text/javascript ' > var forms = document.getElementsByTagName (' form '); for (var i = 0; i< Forms.length; i++) [[var ele = document.createelement (' input '); ele.type= ' hidden '; Ele.id=ele.name= ' {0} '; ele.value= ' {1} '; Forms[i]. AppendChild (ele);] </script>"; stringScript =string. Format (format, Namewithroute, Filtercontext.httpcontext.session[namewithroute]). Replace ("[[","{"). Replace ("]]","}");            FilterContext.HttpContext.Response.Write (script); }}} Copy code

In order to make trading use, the part of creating HiddenField through JavaScript is also encapsulated in Actionfilter, and of course you can add this HiddenField to the motherboard page.

This attribute is then added to the action that needs to be prevented from repeating the commit, and you need to be reminded that both get and post actions need to be added to this attribute, or none.

   [HttpGet]        [noresubmit]        public  actionresult Index ()        {            return  View ();        }        [HttpPost]        [Noresubmit]          Public ActionResult Index (int  ID)        {            return  View ();        }        [Noresubmit]          Public ActionResult About ()        {            return  View ();        }
implementation in ASP. NET Web Form

Like MVC, we can still use the hidden field method in the Web form, but I prefer to use ViewState (view state) to store the flag bit, so you don't have to create hidden field dynamically.

The implementation is placed in a BasePage class that inherits from Web.UI.Page, just inherit the page class from the BasePage.

 Public classbasepage:system.web.ui.page{Private Static ReadOnly stringprefix ="Postflag"; Private stringNamewithroute;  PublicBasePage () {Namewithroute=Generatenamewithroute ();} protected Override voidLoadViewState (Objectsavedstate) {  Object[] Allstates = (Object[]) savedstate; Base. LoadViewState (allstates[0]); intRequestflag =string. IsNullOrEmpty (allstates[1]. ToString ())?0:int. Parse (allstates[1].        ToString ()); intSessionflag = Session[namewithroute] = =NULL?0: (int) Session[namewithroute]; //get or normal post:true;         BOOLIsValid =! IsPostBack | | Sessionflag = =Requestflag; if(Sessionflag = =int. MaxValue) {Sessionflag= -1; } Session[namewithroute]= ++Sessionflag; if(!isValid) {Response.Redirect (Generateurlwithtimestamp (request.rawurl),false);            Response.End (); return; } } protected Override ObjectSaveViewState () {Object[] Allstates =New Object[2]; allstates[0] =Base.        SaveViewState (); allstates[1] =Session[namewithroute]; returnAllstates;} Private stringGeneratenamewithroute () {stringFilesubfix =". aspx"; returnPrefix + Request.FilePath.Replace (filesubfix,""). Replace ("/","_"); }    Private stringGenerateurlwithtimestamp (stringURL) {        return string. Format ("{0}{1}timestamp={2}", URL, url. Contains ("?") ?"&":"?", (Datetime.now-datetime.parse ("2010/01/01")).    Ticks); }}
Avoid bugs in IE 8 and previous versions

A bug in IE8 and earlier versions of IE was found in this process. When redirected to the current path in a refresh operation, two requests, 302 and 200 are generated in Ie9/chrome/firefox, but in IE8, the page becomes blank after 302 is not redirected.

Ie9/chrome/firefox: (normal)

IE8 and Ealier: (Exception)

It is not clear whether this is a IE8 defect or a so-called by-designed behavior. To avoid this, a timestamp is added to the redirected URL in both of these implementations to avoid this problem, unlike the original path.

The limitations of the programme

Since we use the session to store the server-side flag bits, there is a small problem when the user opens the same page in a different tab in the same browser. The tabs that follow open all work fine, but if you do some of the above and then go back to the previously opened tab, it will cause the label redirection to be turned on first.

Prevent duplicate commit issues caused by F5 refreshes in ASP. NET Web form and MVC

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.