Frameworks like Symfony or rails (and probably many others) provide a very convenient feature named RESTful routing, aka HTTP-aware urls and controllers, generally associated with an object/url mapping mechanism to expose Model entities and several available actions on them over HTTP.
This is really useful, especially when you deal with WebServices on a daily basis, or if you want to reuse your controllers in both standard html or service oriented architecture (SOA) contexts.
For example, with Symfony, you can declare an HTTP routes collection this way (all examples are taken from the Sftunes Symfony application I recently released on github):
# routing.yml fortune: class: sfDoctrineRouteCollection options: model: Fortune action: [list, new, create, edit, show] object_actions: comment: post down: put up: put collection_actions: top: get worst: get
fortune route collection will expose the
Fortune Doctrine ORM model over HTTP, using available HTTP verbs like
DELETE for example, hence providing basic CRUD operations (see documentation).
You will then be able to obtain several Symfony routes, to list them just run the
app:routes task from the command line:
~ $ ./symfony app:routes main >> app Current routes for application "main" Name Method Pattern fortune_top GET /fortune/top.:sf_format fortune_worst GET /fortune/worst.:sf_format fortune GET /fortune.:sf_format fortune_new GET /fortune/new.:sf_format fortune_create POST /fortune.:sf_format fortune_edit GET /fortune/:id/edit.:sf_format fortune_update PUT /fortune/:id.:sf_format fortune_delete DELETE /fortune/:id.:sf_format fortune_show GET /fortune/:id.:sf_format fortune_comment POST /fortune/:id/comment.:sf_format fortune_down PUT /fortune/:id/down.:sf_format fortune_up PUT /fortune/:id/up.:sf_format homepage ANY /
By calling an url such as
/fortune/2.html using the
GET verb, you'll obtain a response in text/html format, and Symfony will use a standard HTML template to decorate it -- whereas if you call
/fortune/2.json using the
DELETE verb, you'll ask for the deletion of the fortune related instance and receive a response in JSON format (at least if you provided the related JSON decoration template, of course).
That's pretty fancy, but a common mistake is to generate links from templates calling verbs other than
GET, for example here the
fortune_down routes aim to be called via
PUT (because they imply a modification of a
Fortune object instance), and you may be tempted to write something like this in you templates:
<?php echo link_to('Vote down this fortune', 'fortune_down', $fortune, array( 'method' => 'put', )) ?>
While this is perfectly possible technically speaking, a quick look at the rendered HTML code will temper the interest of this approach:
<a onclick="var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'post'; f.action = this.href; var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', 'sf_method'); m.setAttribute('value', 'put'); f.appendChild(m);var m=document.createElement('input');m.setAttribute('type', 'hidden'); m.setAttribute('name', '_csrf_token'); m.setAttribute('value', 'd26d99f7f4f97lsdhklqejshdjkshdf860124'); f.appendChild(m);f.submit();return false;" href="/main_dev.php/fortune/19/down"> Vote down this fortune </a>
link_to() with the
method option set to something else than
GET will generate a form to challenge the url with the correct HTTP verb (through the kinda magic
GET verb, because it's just a link to another resource in view mode, not a modification of it. So you should rather use a
<form/> tag to deal with such kind of operations in your code, always.
But there's more: imagine you want to deal with a
Fortune modification form, still by using the
PUT http verb and the
fortune_update route, in a standard html context (not a WebService one); you have a form so it's okay? It's not, a browser, even the most modern one in 2010, will not understand something else than
POST. That's a shame actually. Symfony circumvents the problem by adding a supplementary
sf_method hidden parameter to the form fields, so the targeted controller will be able to detect an incoming
PUT request, but this is clearly a kind of patch applied to HTTP support in Browsers.
I'm searching for a conclusion, but can't find one except why on Earth modern browsers don't deal with something else than
Feel free to provide hints on this topic in the comments.
Edit: Just learnt via the comments that HTML5 draft spec includes support of
DELETE HTTP verbs in forms (source - thx jblanche).
Great, can't wait for 2022!