1 package Catalyst::TraitFor::Request::REST::ForBrowsers;
3 use namespace::autoclean;
5 with 'Catalyst::TraitFor::Request::REST';
8 $VERSION = eval $VERSION;
10 has _determined_real_method => (
15 has looks_like_browser => (
19 builder => '_build_looks_like_browser',
23 # All this would be much less gross if Catalyst::Request used a builder to
24 # determine the method. Then we could just wrap the builder.
25 around method => sub {
29 return $self->$orig(@_)
30 if @_ || $self->_determined_real_method;
32 my $method = $self->$orig();
35 if ( defined $method && uc $method eq 'POST' ) {
36 $tunneled = $self->param('x-tunneled-method')
37 || $self->header('x-http-method-override');
40 $self->$orig( defined $tunneled ? uc $tunneled : $method );
42 $self->_determined_real_method(1);
44 return $self->$orig();
48 my %HTMLTypes = map { $_ => 1 } qw(
53 sub _build_looks_like_browser {
56 my $with = $self->header('x-requested-with');
58 if $with && grep { $with eq $_ }
59 qw( HTTP.Request XMLHttpRequest );
61 if ( uc $self->method eq 'GET' ) {
62 my $forced_type = $self->param('content-type');
64 if $forced_type && !$HTMLTypes{$forced_type};
67 # IE7 does not say it accepts any form of html, but _does_
68 # accept */* (helpful ;)
70 if $self->accepts('*/*');
73 if grep { $self->accepts($_) } keys %HTMLTypes;
76 if @{ $self->accepted_content_types() };
78 # If the client did not specify any content types at all,
79 # assume they are a browser.
92 Catalyst::TraitFor::Request::REST::ForBrowsers - A request trait for REST and browsers
98 use namespace::autoclean;
101 use CatalystX::RoleApplicator;
105 __PACKAGE__->apply_request_class_roles(qw[
106 Catalyst::TraitFor::Request::REST::ForBrowsers
111 Writing REST-y apps is a good thing, but if you're also trying to support web
112 browsers, you're probably going to need some hackish workarounds. This module
113 provides those workarounds for you.
115 Specifically, it lets you do two things. First, it lets you "tunnel" PUT and
116 DELETE requests across a POST, since most browsers do not support PUT or
117 DELETE actions (as of early 2009, at least).
119 Second, it provides a heuristic to check if the client is a web browser,
120 regardless of what content types it claims to accept. The reason for this is
121 that while a browser might claim to accept the "application/xml" content type,
122 it's really not going to do anything useful with it, and you're best off
127 This class provides the following methods:
129 =head2 $request->method
131 This method works just like C<< Catalyst::Request->method() >> except it
132 allows for tunneling of PUT and DELETE requests via a POST.
134 Specifically, you can provide a form element named "x-tunneled-method" which
135 can override the request method for a POST. This I<only> works for a POST, not
138 You can also use a header named "x-http-method-override" instead (Google uses
139 this header for its APIs).
141 =head2 $request->looks_like_browser
143 This attribute provides a heuristic to determine whether or not the request
144 I<appears> to come from a browser. You can use this however you want. I
145 usually use it to determine whether or not to give the client a full HTML page
146 or some sort of serialized data.
148 This is a heuristic, and like any heuristic, it is probably wrong
149 sometimes. Here is how it works:
155 If the request includes a header "X-Request-With" set to either "HTTP.Request"
156 or "XMLHttpRequest", this returns false. The assumption is that if you're
157 doing XHR, you don't want the request treated as if it comes from a browser.
161 If the client makes a GET request with a query string parameter
162 "content-type", and that type is I<not> an HTML type, it is I<not> a browser.
166 If the client provides an Accept header which includes "*/*" as an accepted
167 content type, the client is a browser. Specifically, it is IE7, which submits
168 an Accept header of "*/*". IE7's Accept header does not include any html types
173 If the client provides an Accept header and accepts either "text/html" or
174 "application/xhtml+xml" it is a browser.
178 If it provides an Accept header of any sort that doesn't match one of the
179 above criteria, it is I<not> a browser.
183 The default is that the client is a browser.
187 This all works well for my apps, but read it carefully to make sure it meets
188 your expectations before using it.
192 Dave Rolsky, C<< <autarch@urth.org> >>
196 Please report any bugs or feature requests to
197 C<bug-catalyst-action-rest@rt.cpan.org>, or through the web interface at
198 L<http://rt.cpan.org>. We will be notified, and then you'll automatically be
199 notified of progress on your bug as I make changes.
201 =head1 COPYRIGHT & LICENSE
203 Copyright 2008-2010 Dave Rolsky, All Rights Reserved.
205 This program is free software; you can redistribute it and/or modify it under
206 the same terms as Perl itself.