Ad-Hockery

ad-hockery: /ad·hok'@r·ee/, n.
Gratuitous assumptions... which lead to the appearance of semi-intelligent behavior but are in fact entirely arbitrary. Jargon File

Semi-RESTful Scaffolded Controllers

The default Grails scaffolded controllers use a create action to render a form and a save action as the target the form will POST to. If the save action succeeds it redirects to show and if it fails due to constraint violations it re-renders the form. The same applies to editing with the edit and update actions.

There’s a slight quirk here in that a failed save will cause the URL to change from /controllerName/create to /controllerName/save. Not a particularly huge issue, after all it’s not something a search spider will see and users will be very unlikely to care. One thing I have found problematic though is when rendering a navigation element that highlights an item based on the current action.

Imagine a bit of GSP code like this:

<nav>
    <g:link action="list" class="${actionName == 'list' ? 'active' : ''}">List Cocktails</g:link>
    <g:link action="create" class="${actionName == 'create' ? 'active' : ''}">Create Cocktail</g:link>
    <g:link action="search" class="${actionName == 'search' ? 'active' : ''}">Search Cocktails</g:link>
</nav>

This creates a navigation element with links to the various controller actions & highlights the current action. However, if we’ve submitted bad data from the create form no navigation element will be highlighted. With scaffolding this is easy to fix by changing the test to actionName in ['create', 'save'] but with custom controllers the relationship between actions might not always be so predictable and the navigation template will need frequent tweaking to stay up to date as the code evolves.

With the Twitter Bootstrap scaffolding example I created recently I used a different pattern in the scaffolded controller template:

def create() {
    switch (request.method) {
    case 'GET':
        // render the form
    case 'POST':
        // save the object
        // if successful redirect
        // otherwise re-render the form
    }
}

Effectively I’ve merged the create and save actions. The form posts back to the create action instead of save. This means the URL only changes when we successfully redirect away from creating our object & the create navigation entry stays highlighted if we screw up creating our object.

I’ve done the same with the edit and update actions. Have a look at the full source on GitHub if you’re interested.

It feels like a step closer to a RESTful interface. I’m using the HTTP verbs to determine what to do with requests to the same URL. Full RESTful mapping isn’t possible as forms will only GET or POST not PUT or DELETE. Also using Grails' RESTful URL mappings would defeat the point as the action name in the GSP would be different.

This is neat enough but a couple of objections occur:

  • I don’t particularly like the switch statement, but controller actions should be kept trivial anyway (controllers are for routing requests around, not performing complex logic).

  • Unit testing becomes marginally more awkward as we have to set request.method rather than just invoking a different action.

  • This is a sledgehammer to crack a nut & only a pedant would care. Yeah, fair enough.

Web Statistics