Prevent repeated submission caused by F5 refreshing in ASP. NET web form and MVC.

Source: Internet
Author: User
Tags blank page
What is Refresh/reload?

Refresh in IE is called reload in ff and chrome. The difference between reload and normal page access is as follows:

1. Cache Control

If a file (slice) already exists in the local cache, normal access to the page will directly load from the local without accessing the server. For the refresh operation, even if the local cache exists, the server will be forcibly accessed to check for updates. The request header will contain the "if-modified-since" label. If the file is not updated, the server returns 304; otherwise, the server returns 200 and the updated file.

2. Submit again

Refresh will submit the last request again. For example, you can create an order and save it. After the system operation is successful, it stays on the current page. Refresh now. the browser will pop up a dialog box asking if you want to continue. After clicking OK, the browser will submit the last post request again (click to save the request ), the system may generate a duplicate order. Of course, if the last operation was only a GET request rather than a POST request, repeated submission would not cause this problem.

How to prevent repeated submission

The method to prevent repeated submission has some similarities with the PRG (post-redirect-Get) mode. Specifically, when the web server identifies a repeated POST request, redirect to the current page, and then the browser requests the page in get mode. If you use tools such as httpwatch to view network requests, you can see two requests, 302 & 200.

The problem now is how to identify a request that is caused by refresh. The refresh operation will repeat the previous request, instead of the features of the current request. The following solutions are available: Session and page (which can be a hidden field on the page) set an integer flag, compare the values of the two in each POST request, and then auto-increment. Under normal submission, the browser will submit the value of the hidden field on the current page, so each time this value is equal to the value in the session; When refreshing, the submitted value is the value of the previous request, which is inconsistent with the session. It is known that the refresh operation causes repeated submission.

This is similar to the token verification in struts.

Here we call the page hidden field client flag. The specific process is as follows:

When refreshing the page in the last step, the value of the hidden field is 3, but the browser will submit the last request again (2), causing inconsistency with the value (3) in the session.

Implementation in ASP. NET MVC

Add a custom actionfilter.

View code

  Public   Class  Noresubmitattribute: actionfilterattribute {  Private   Static  Readonly   String Httpmehotdpost = "  Post  "  ;  Private   Static   Readonly   String Prefix = "  Postflag  "  ;  Private   String Namewithroute;  Public   Override   Void  Onactionexecuting (actionexecutingcontext filtercontext ){  VaR Controllercontext = Filtercontext. Controller. controllercontext;  If (! Controllercontext. ischildaction ){  VaR Request = Controllercontext. httpcontext. request;  VaR Session = Controllercontext. httpcontext. Session; namewithroute = Generatenamewithroute (controllercontext );  Int Sessionflag = session [namewithroute] = Null ? 0 :( Int  ) Session [namewithroute];  Int Requestflag = String . Isnullorempty (request. Form [namewithroute])? 0 : Int . Parse (request. Form [namewithroute]);  //  Get or normal post: true;                  Bool Isvalid =! Ispost (filtercontext) | sessionflag = Requestflag;  If (Sessionflag = Int  . Maxvalue) {sessionflag =- 1  ;} Session [namewithroute] = ++ Sessionflag; If (! Isvalid) {filtercontext. Result = New  Redirectresult (generateurlwithtimestamp (request. rawurl ));  Return  ;}}  Base  . Onactionexecuting (filtercontext );}  Private   String Generateurlwithtimestamp ( String  URL ){ Return   String . Format ( "  {0} {1} timestamp = {2}  " , URL, URL. Contains ( "  ?  " )? "  &  " : "  ?  " , (Datetime. Now-datetime. parse ( " 2010/01/01  "  ). Ticks );}  Private   Bool  Ispost (actionexecutingcontext filtercontext ){  Return Filtercontext. httpcontext. Request. httpmethod = Httpmehotdpost ;}  Private   String  Generatenamewithroute (controllercontext) {stringbuilder sb = New Stringbuilder (prefix );  Foreach ( Object Routevalue In  Controllercontext. routedata. Values. Values) {sb. appendformat (  "  _ {0}  "  , Routevalue );}  Return  SB. tostring ();}  Public   Override   Void Onresultexecuted (resultexecutedcontext filtercontext ){  Base  . Onresultexecuted (filtercontext );  If (! Filtercontext. ischildaction &&! (Filtercontext. Result Is  Redirectresult )){  //  String format = "<SCRIPT type = 'text/JavaScript '> $ (function () [[$ ('form '). each (function () [[$ ('<input type = hidden id = {0} name = {0} value = {1}/> '). appendto ($ (this);]); </SCRIPT> ";                  String Format = " <SCRIPT type = 'text/JavaScript '> var forms = document. getelementsbytagname ('form'); For (VAR I = 0; I <forms. length; I ++) [[Var ele = document. createelement ('input'); ELE. type = 'siden'; ELE. id = ELE. name = '{0}'; ELE. value = '{1}'; forms [I]. appendchild (Ele);] </SCRIPT>  "  ;  String Script = String . Format (format, namewithroute, filtercontext. httpcontext. session [namewithroute]). Replace ( "  [[  " , "  { " ). Replace ( "  ]  " , "  }  "  ); Filtercontext. httpcontext. response. Write (SCRIPT );}}} 

For ease of submission, the section for creating hiddenfield through JavaScript is also encapsulated in actionfilter. Of course, you can also add this hiddenfield on the parent page.

Add this attribute to the action to prevent repeated submissions,Note that this attribute must be added for get and post actions, or none of them must be added..

View code

[Httpget] [noresubmit]PublicActionresult index (){ReturnView ();} [httppost] [noresubmit]PublicActionresult index (IntID ){ReturnView ();} [noresubmit]PublicActionresult about (){ReturnView ();}

 

Implementation in ASP. NET web form

Like MVC, we can still use the hidden field method in web form, but I prefer to use viewstate to store this flag, in this way, you are not allowed to dynamically create a hidden field.

The implementation is placed in a basepage class inherited from web. UI. Page. You only need to inherit the basepage from the page class.

View code

 Public   Class  Basepage: system. Web. UI. Page {  Private   Static   Readonly   String Prefix = "  Postflag  "  ; Private   String  Namewithroute;  Public  Basepage () {namewithroute = Generatenamewithroute ();}  Protected   Override   Void Loadviewstate ( Object  Savedstate ){  Object [] Allstates = ( Object  []) Savedstate; Base . Loadviewstate (allstates [ 0  ]);  Int Requestflag = String . Isnullorempty (allstates [ 1 ]. Tostring ())? 0 : Int . Parse (allstates [ 1  ]. Tostring ());  Int Sessionflag = session [namewithroute] = Null ? 0 :( Int  ) Session [namewithroute];  //  Get or normal post: true;          Bool Isvalid =! 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   Object  Saveviewstate (){  Object [] Allstates = New   Object [ 2 ]; Allstates [  0 ] = Base  . Saveviewstate (); allstates [  1 ] = Session [namewithroute];  Return  Allstates ;}  Private   String  Generatenamewithroute (){  String Filesubfix = "  . Aspx "  ;  Return Prefix + request. filepath. Replace (filesubfix, "" ). Replace ( "  /  " , "  _  "  );}  Private   String Generateurlwithtimestamp ( String  URL ){ Return   String . Format ( "  {0} {1} timestamp = {2}  " , URL, URL. Contains ( "  ?  " )? "  &  " : "  ?  " , (Datetime. Now-datetime. parse ( " 2010/01/01  "  ). Ticks );}} 

 

Avoid bugs in IE 8 and earlier versions

In this process, we found a bug in IE8 and earlier ie versions. In the refresh operation, redirect to the current path. ie9/Chrome/Firefox will generate two requests, 302 and 200, but in IE8, no redirection is performed after 302, the page becomes a blank page.

Ie9/Chrome/Firefox: (normal)

IE8 and ealier: (exception)

It is unclear whether this is a defect of IE8 or a behavior called by-designed. To avoid this problem, a timestamp is added to the Redirection URL in the preceding two implementations so that the timestamp is different from the original path to avoid this problem.

Restrictions of the Solution

Because session is used to store the server's flag space, it may cause minor problems when users open the same page on different tabs in the same browser. The tabs opened later are all normal, but if you perform some operations above and then return to the previously opened tag operation, the first opened tag redirection will occur.

Related Article

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.