Catalyst + REST

jk2addict on 2008-01-17T14:24:05

Since we're on the topic of Catalyst and REST, I hereby declare Firefox 2.x broken.

There's this great set of modules under the moniker Catalyst::Action::REST that easily adds REST support to Catalyst. While I have no REST API in Mango (yet), I also use this package to do Content-Type -> View negotiations (text/html-> View::HTML, application/xhtml+xml->View::XHTML, application/atom+xml->View::Atom, etc), and it does it quite well.

Unfortunately, Firefox 2.x throws a kink in the works. The REST controller looks at the Content-Type header, then falls back to the content-type query param, then falls back to the Accept header. It's that last thing that kills. You see, firefox sends this accept header:

text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5


So, when I request a regular old web page, really wanting HTML, the REST controller negotiation falls back to text/xml, even though my default type in config is text/html. Grrr.

There are at least two solutions to this [for me in my base class]:

1. Cleanse the Accept header of this madness before calling the REST package into action

2. Map text/xml to my HTML View

Both are evil in their own ways. I started with #2, since people should really be sending application/xml anyhow and as of now, my app doesn't support xml via REST/content negotiation anyways.

#1 is really the better answer since we can't fix Firefox 2.x. For REST clients, they must send a Content-Type header, so those aren't a problem. And you can always override using the content-type param.

Just for giggles, Firefox 3.x beta Accept header is:

text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8


Browsers suck

autarch on 2008-01-17T15:10:29

IE7 doesn't say it accepts any form of HTML, but it _does_ accept "*/*". I'm using Catalyst::Action::REST as well but I've customized it so that if the request is a GET request and the request seems to be from a browser, I always return HTML. This can be overridden by setting a "content-type" query string param so that I can test manually from my browser.

The way I determine if a request is from a browser is simply to check if an HTML type (text/html or application/xhtml+xml) is in the list of accepted content types or if "*/*" is in the list. If it is I return HTML.

My theory is that a "real" REST client will specify just one content type that it wants, and that type won't be HTML.

Re:Browsers suck

jk2addict on 2008-01-17T15:23:12

Now that I think about your response and the issue a little more, it seems even selecting text/html just because it's in the Accept is risky.

I'd wager now, the best approach would be a config option to tell REST to ignore the Accept header altogether and instead go with the default type in config (even per controller).

Re:Browsers suck

sjn on 2008-01-18T10:59:18

Yeah, this is an unfortunate workaround for IE. At least Firefox gets it right. (Now, we just have to make Catalyst::Request::REST do the right thing ;-)

This is a bug in Catalyst::Request::REST

sjn on 2008-01-18T10:56:42

Check out RT bug #23772. The right solution to this is to let the server decide which of the highest-weighted Accept:'ed content-types to send.

Of course IE breaks this by not even stating that it accepts text/html, but this is definitely not a Firefox bug.

And Firefox 3.0's Accept headers look really sexeh to me. Definitive improvement. ^^