Jonathan Hanson.org

Revision

Jonathan Hanson

May 2nd, 2010

If you're creating a RESTful framework, at some point you're going to need to use verbs other than GET and POST.  The problem is, none of the browsers available today will submit a form with anything other than those two HTTP request methods.

I tried using AJAX to handle the submissions for me, but there's the problem of what to do with the response that comes back from the request.  If you're thinking of trying this strategy, don't.  You won't be able to fake an actual page refresh effectively in Javascript.  The onLoad events won't fire predictably, and the browser's URL bar won't change.

There is a simple and effective cheat, however.  I found it referred to as "tunneling over POST" and it works like this.  For forms with a method other than GET or POST, you add a hidden field to contain the desired HTTP method, and then you change the form method to POST.  Then, on the server, you check for a condition where the HTTP method is POST and the hidden field is present.  In this circumstance, you interpret the request method from the hidden field, and not from the actual request method.

Of course this is a cheat and the actual method being used is POST, so if any intermediary logic depends on the method really being DELETE or PUT, then this will fail.  But if all you care about is planning for the future when browsers will natively support PUT and DELETE, then this hack lets you write your framework in such a way that you can remove this shim easily down the road.

So how do you actually do it?  Here's how.  I assume in this example that jQuery is available on the client and PHP is running on the server, but the same strategy could be used with any mixture of client and server script environments.  On the client in Javascript, i always run this code:

$( function() {
	
	// For forms of the unsupported request methods
	$('form[method!="POST"]').filter('form[method!="GET"]').each( function() {
		
		// insert a hidden field that will alert the server that this is "tunneled" over POST
		$(this).prepend('<input type="hidden" name="_tunneled_http_method" value="'+$(this).attr('method')+'" />');
		
		// Change the method to POST, to make sure the form is safe
		$(this).attr('method', 'POST');
	}); 
	
});

Then, on the server side before any logic that might care about the HTTP request method runs, I do this:

// Clean up the HTTP methods tunneled over POST hack
if( strtoupper($_SERVER['REQUEST_METHOD'])=='POST' && array_key_exists('_tunneled_http_method', $_POST) ) {
	$_SERVER['REQUEST_METHOD'] = $_POST['_tunneled_http_method'];
	unset($_POST['_tunneled_http_method']);
}

If the request method is POST with the hidden "_tunneled_http_method" field present, then replace the superglobal $_SERVER['REQUEST_METHOD'] variable with the value from the hidden field, and then unset the hidden field.  Bam, it's like it never happened.  

And when browsers start supporting DELETE and PUT in HTML5, all you have to do is remove the javascript shim.  Or, if you can think of a way to determine in Javascript whether the browser supports methods other than GET and POST, you could probably mod the javascript example above to not run in the event of native browser support.