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:
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:
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
Luckily for me I'd restricted usernames with the constraint
So now, I can use the same constraint on the controller:
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:
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>