Sunday, 1 June 2008

Grails, favicon, and urls

I've just spent a little too much time trying to get my favicon working properly on a new grails application.

The issue I encountered was due to the fact that I wanted users to be able to access their accounts using '/[username]' - i.e. /paul

To accomodate this, I used a UrlMapping:
"/$id" {
  controller='display'
  action:'index'
}

Now, this meant that /favicon.ico would hit the controller, and not server the icon file. I first thought I could get around this by constraining the above mapping - so I tried:
"/$id" {
  controller='display'
  action:'index'
  constraints {
    id(notEqual:'favicon.ico')
  }
}
It seems though that the only constraint you can use is 'matches'. At least 'notEqual' resulted in a compile error, and all of the examples use 'matches'.

So I thought I'd use matches with a regular expression of to exclude the word 'favicon.ico' - turns out that matching a word is simple, but negating it is not - I couldn't find an expression to negate a word.

Anyway, I've noticed that when requesting '/favicon.ico', the $id parameter resolves to 'favicon' - the extension has been stripped off! Luckily I stumbled across a forum post pointing me to the Content Negotiation section of the user guide. You can turn off this behaviour with
  grails.mime.file.extensions = false

Luckily for me I'd restricted usernames with the constraint
matches:"[a-zA-Z0-9_-]+"

So now, I can use the same constraint on the controller:
"/$id" {
  controller='display'
  action:'index'
  constraints {
    id(matches:/[a-zA-Z0-9_-]+/)
  }
}
Since the request for '/favicon.ico' contains a dot, it fails this constraint and the controller no longer handles the request.

I wish I'd come to this conclusion more quickly (I also investigated what I could do on the Apache side of things, but didn't get anywhere), but I'm glad I've learnt what I've learnt.

Note, you don't necessarily need to do this - you can use the following code to specify an alternate url to the icon:
<link href="/common/apps/op/favicon.ico" rel="shortcut icon" type="image/x-icon"></link>