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 sub _build_accepted_content_types {
32 # First, we use the content type in the HTTP Request. It wins all.
33 $types{ $self->content_type } = 3
34 if $self->content_type;
36 if ($self->method eq "GET" && $self->param('content-type')) {
37 $types{ $self->param('content-type') } = 2;
40 # Third, we parse the Accept header, and see if the client
41 # takes a format we understand.
43 # This is taken from chansen's Apache2::UploadProgress.
44 if ( $self->header('Accept') ) {
45 $self->accept_only(1) unless keys %types;
47 my $accept_header = $self->header('Accept');
50 foreach my $pair ( split_header_words($accept_header) ) {
51 my ( $type, $qvalue ) = @{$pair}[ 0, 3 ];
52 next if $types{$type};
54 # cope with invalid (missing required q parameter) header like:
55 # application/json; charset="utf-8"
56 # http://tools.ietf.org/html/rfc2616#section-14.1
57 unless ( defined $pair->[2] && lc $pair->[2] eq 'q' ) {
61 unless ( defined $qvalue ) {
62 $qvalue = 1 - ( ++$counter / 1000 );
65 $types{$type} = sprintf( '%.3f', $qvalue );
69 [ sort { $types{$b} <=> $types{$a} } keys %types ];
72 sub _build_preferred_content_type { $_[0]->accepted_content_types->[0] }
78 return grep { $_ eq $type } @{ $self->accepted_content_types };
86 Catalyst::TraitFor::Request::REST - A role to apply to Catalyst::Request giving it REST methods and attributes.
90 if ( $c->request->accepts('application/json') ) {
94 my $types = $c->request->accepted_content_types();
98 This is a L<Moose::Role> applied to L<Catalyst::Request> that adds a few
99 methods to the request object to facilitate writing REST-y code.
100 Currently, these methods are all related to the content types accepted by
109 If the request went through the Deserializer action, this method will
110 return the deserialized data structure.
112 =item accepted_content_types
114 Returns an array reference of content types accepted by the
117 The list of types is created by looking at the following sources:
121 =item * Content-type header
123 If this exists, this will always be the first type in the list.
125 =item * content-type parameter
127 If the request is a GET request and there is a "content-type"
128 parameter in the query string, this will come before any types in the
131 =item * Accept header
133 This will be parsed and the types found will be ordered by the
134 relative quality specified for each type.
138 If a type appears in more than one of these places, it is ordered based on
139 where it is first found.
141 =item preferred_content_type
143 This returns the first content type found. It is shorthand for:
145 $request->accepted_content_types->[0]
149 Given a content type, this returns true if the type is accepted.
151 Note that this does not do any wildcard expansion of types.
157 See L<Catalyst::Action::REST> for authors.
161 You may distribute this code under the same terms as Perl itself.