Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Is implementing the PRG pattern the 'standard' way?

Status
Not open for further replies.

theEclipse

Programmer
Dec 27, 1999
1,190
US
I have been working on a custom CMS/shopping cart to fulfill some specific customer needs and find myself constantly clicking "Resend" when asked by my browser if it is okay to resubmit.

I know that can be avoided by posting via GET, but I have noticed that not many websites (eg. Paypal) use that as it can still lead to double form submittals. However, neither do they process things in such a way as I would be able to resubmit form data.

I came across and I am really unsure about the idea of redirecting every form that is submitted via POST, especially when the idea collides with error checking.

I searched google for some example code but can't seem to produce anything more than high-level abstractions, searching TT::pHP for 'post redirect get' yielded some results but seemingly nothing relevant.

What is the normal way of preventing these messages and concurrently preventing double form submittals?

Robert Carpenter
Remember....eternity is much longer than this ~80 years we will spend roaming this earth.
ô¿ô
 
personally i do not use PRG. the rules of thumb that i use is as follows:

GET for idempotent actions. i.e. actions that do not 'DO' anything (such as database activities) - NOTE I have one exception to this rule (see below).

POST for everything else.

to protect against double actions i attach a nonce to all actions that are not idempotent. a nonce is a number used once. you set a nonce as a hidden field in the form and in the session handler. then compare their values before allowing a database (or non idempotent) action. search for wordpress nonces for good explanations on what these are. i have also published a simple nonce handler. it is found here as part of my non-image based captcha post. A 'proper' nonce handler will store a reference to the precise action that is to be permitted, and the time for which the action will be permitted. you can see that this is a very simple overlay on my code.

the exception to my general rule is for delete actions. i prefer delete actions to be triggered via a simple hyperlink coupled with some JS confirmation dialog. the reason is partly coding ease and partly that i don't care if a delete action is committed twice. as the second and subsequent actions are idempotent in themselves (nothing left to delete). to be 'proper' and less lazy i should add a simple ajax handler to the link to post the delete request rather than send it via GET.

note that a 'proper' nonce implementation will stop you from resubmitting forms whilst bug checking (or rather stop the resubmission from having any effect). the workaround for this is simply to stub out the nonce checking script to return true before processing. just remember to remove the stub before publshing.

the 'resend messages'. more difficult, frankly, as they are generated by the browser rather than anything that you can control. one easy workaround, however, is to deploy jQuery core in your pages and just add an event handler to submit the forms by ajax rather than via a browser refresh. off the top of my head some simple code such as this should sort you out (untested).

Code:
jQuery(document).ready(function(){
	rebind();
});

function rebind(){
	jQuery("form").bind(	
			'submit', 
			function(e){
				e.stopPropagation(); //stop event propagation
				e.preventDefault(); //stop the form from submitting
				jQuery.post(
					this.attrib('action'), //url
 					this.serialize(), //data to be posted
 					replacePage(t), 
					'html');
				return false;
			});
}

function replacePage(t){
	document.write(t);
	rebind();
}

function newPage(t){
	var w = jQuery(window).open();
	w.html(t);
}

function popUp(t){
	jQuery('body').append('<div id="popup" style="display: none;"></div><div id="window" style="display: none;"><div id="popup_content"><a href="#" onclick="Close_Popup();">Close</a>' + t + '</div></div>');
	jQuery('#popup').css(	{'height': '100%',
							'width' : '100%',
							'background': '#000000',
							'position': 'absolute',
							'top': '0',
							'-moz-opacity':'0.75',
							'-khtml-opacity': '0.75',
							'opacity': '0.75',
							'filter':'alpha(opacity=75)'});
	jQuery('#window').css(	{'width': '600px',
							'height': '300px',
							'margin': '0 auto',
							'border': '1px solid #000000',
							'background': '#ffffff',
							'position': 'absolute',
							'top': '200px',
							'left': '25%'});
	jQuery('#popup').fadeIn(	'fast', 
								jQuery('#windows').fadeIn('fast')
							);
}
function closeUp(){
	jQuery('#window').fadeOut(	'fast', 
								jQuery('#popup').fadeOut(	'fast',
															jQuery('#popup').remove()));
}

please note that if you are not using any other js frameworks you may be able to substitute the jQuery references with a $ sign. i prefer the longhand, however.

if i have misunderstood the requirement for the ajax code then another alternative is the ajax method but write the output in a dialog (change the replacePage(t) reference to popUp(t) ) or to open the resulting page in a new window (change the replacePage(t) to a newPage(t))

as said, the above code is totally untested so feel free to post back with bug reports.

the popup styles and html are thanks to this site
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top