1 package Catalyst::TraitFor::Request::REST;
3 use HTTP::Headers::Util qw(split_header_words);
4 use namespace::autoclean;
7 $VERSION = eval $VERSION;
9 has [qw/ data accept_only /] => ( is => 'rw' );
11 has accepted_content_types => (
15 builder => '_build_accepted_content_types',
19 has preferred_content_type => (
23 builder => '_build_preferred_content_type',
27 has accepted_response_content_types => (
31 builder => '_build_accepted_response_content_types',
35 has preferred_response_content_type => (
39 builder => '_build_preferred_response_content_type',
43 sub _accepted_types_sort {
44 my ($self, %types) = @_;
45 [ sort { $types{$b} <=> $types{$a} } keys %types ];
48 sub _build_accepted_content_types {
50 my %types = $self->_accepted_response_content_types_inner;
51 # First, we use the content type in the HTTP Request. It wins all.
52 $types{ $self->content_type } = 3
53 if $self->content_type;
54 $self->_accepted_types_sort(%types);
57 sub _build_accepted_response_content_types {
59 my %types = $self->_accepted_response_content_types_inner;
60 $self->_accepted_types_sort(%types);
63 sub _accepted_response_content_types_inner {
68 if ($self->method eq "GET" && $self->param('content-type')) {
69 $types{ $self->param('content-type') } = 2;
72 # Third, we parse the Accept header, and see if the client
73 # takes a format we understand.
75 # This is taken from chansen's Apache2::UploadProgress.
76 if ( $self->header('Accept') ) {
77 $self->accept_only(1) unless keys %types;
79 my $accept_header = $self->header('Accept');
82 foreach my $pair ( split_header_words($accept_header) ) {
83 my ( $type, $qvalue ) = @{$pair}[ 0, 3 ];
84 next if $types{$type};
86 # cope with invalid (missing required q parameter) header like:
87 # application/json; charset="utf-8"
88 # http://tools.ietf.org/html/rfc2616#section-14.1
89 unless ( defined $pair->[2] && lc $pair->[2] eq 'q' ) {
93 unless ( defined $qvalue ) {
94 $qvalue = 1 - ( ++$counter / 1000 );
97 $types{$type} = sprintf( '%.3f', $qvalue );
104 sub _build_preferred_content_type { $_[0]->accepted_content_types->[0] }
106 sub _build_preferred_response_content_type { $_[0]->accepted_response_content_types->[0] }
112 return grep { $_ eq $type } @{ $self->accepted_content_types };
120 Catalyst::TraitFor::Request::REST - A role to apply to Catalyst::Request giving it REST methods and attributes.
124 if ( $c->request->accepts('application/json') ) {
128 my $types = $c->request->accepted_content_types();
132 This is a L<Moose::Role> applied to L<Catalyst::Request> that adds a few
133 methods to the request object to facilitate writing REST-y code.
134 Currently, these methods are all related to the content types accepted by
143 If the request went through the Deserializer action, this method will
144 return the deserialized data structure.
146 =item accepted_content_types
148 Returns an array reference of content types accepted by the
151 The list of types is created by looking at the following sources:
155 =item * Content-type header
157 If this exists, this will always be the first type in the list.
159 =item * content-type parameter
161 If the request is a GET request and there is a "content-type"
162 parameter in the query string, this will come before any types in the
165 =item * Accept header
167 This will be parsed and the types found will be ordered by the
168 relative quality specified for each type.
172 If a type appears in more than one of these places, it is ordered based on
173 where it is first found.
175 =item preferred_content_type
177 This returns the first content type found. It is shorthand for:
179 $request->accepted_content_types->[0]
183 Given a content type, this returns true if the type is accepted.
185 Note that this does not do any wildcard expansion of types.
191 See L<Catalyst::Action::REST> for authors.
195 You may distribute this code under the same terms as Perl itself.