Commit | Line | Data |
774437bb |
1 | package Catalyst::RequestRole::REST; |
2 | # ABSTRACT: A REST-y role for Catalyst::Request |
3 | use Moose::Role; |
4 | |
5 | use Catalyst::Utils; |
6 | use HTTP::Headers::Util qw(split_header_words); |
7 | use namespace::clean -except => 'meta'; |
8 | |
9 | has accept_only => ( |
bcf4a936 |
10 | is => 'rw', |
774437bb |
11 | isa => 'Bool', |
bcf4a936 |
12 | # writer => '_set_accept_only', FIXME fails for me if I use this |
774437bb |
13 | default => 0, |
14 | ); |
15 | |
16 | has accepted_content_types => ( |
17 | is => 'ro', |
18 | isa => 'ArrayRef', |
19 | init_arg => undef, |
20 | lazy_build => 1, |
21 | ); |
22 | |
23 | has _accepted_content_types_hash => ( |
24 | is => 'ro', |
25 | isa => 'HashRef', |
26 | init_arg => undef, |
27 | lazy_build => 1, |
28 | ); |
29 | |
30 | sub _build_accepted_content_types { |
31 | my $self = shift; |
32 | my %types; |
33 | |
34 | # First, we use the content type in the HTTP Request. It wins all. |
35 | $types{ $self->content_type } = 3 if $self->content_type; |
36 | |
37 | if ($self->method eq "GET" && |
38 | (my $ct = $self->params->{'content-type'})) { |
39 | $types{ $ct } = 2; |
40 | } |
41 | |
42 | # Third, we parse the Accept header, and see if the client |
43 | # takes a format we understand. |
44 | # |
45 | # This is taken from chansen's Apache2::UploadProgress. |
46 | if ( $self->header('Accept') ) { |
bcf4a936 |
47 | $self->accept_only(1) unless keys %types; # FIXME fails if _set_accept_only |
774437bb |
48 | |
49 | my $accept_header = $self->header('Accept'); |
50 | my $counter = 0; |
51 | |
52 | foreach my $pair ( split_header_words($accept_header) ) { |
53 | my ( $type, $qvalue ) = @{$pair}[ 0, 3 ]; |
54 | next if $types{$type}; |
55 | |
56 | unless ( defined $qvalue ) { |
57 | $qvalue = 1 - ( ++$counter / 1000 ); |
58 | } |
59 | |
60 | $types{$type} = sprintf( '%.3f', $qvalue ); |
61 | } |
62 | } |
63 | |
64 | return [ sort { $types{$b} <=> $types{$a} } keys %types ]; |
65 | } |
66 | |
67 | sub preferred_content_type { $_[0]->accepted_content_types->[0] } |
68 | |
69 | sub _build__accepted_content_types_hash { |
70 | return { map {; $_ => 1 } @{ $_[0]->accepted_content_types } }; |
71 | } |
72 | |
73 | sub accepts { $_[0]->_accepted_content_types_hash->{$_[1]} } |
74 | |
75 | 1; |
76 | |
77 | __END__ |
78 | |
79 | =head1 SYNOPSIS |
80 | |
81 | if ( $c->request->accepts('application/json') ) { |
82 | ... |
83 | } |
84 | |
85 | my $types = $c->request->accepted_content_types(); |
86 | |
87 | =head1 DESCRIPTION |
88 | |
89 | This is a subclass of C<Catalyst::Request> that adds a few methods to |
90 | the request object to faciliate writing REST-y code. Currently, these |
91 | methods are all related to the content types accepted by the client. |
92 | |
93 | Note that if you have a custom request class in your application, and it does |
94 | not inherit from C<Catalyst::Request::REST>, your application will fail with an |
95 | error indicating a conflict the first time it tries to use |
96 | C<Catalyst::Request::REST>'s functionality. To fix this error, make sure your |
97 | custom request class inherits from C<Catalyst::Request::REST>. |
98 | |
99 | =method accepted_content_types |
100 | |
101 | Returns an array reference of content types accepted by the |
102 | client. |
103 | |
104 | The list of types is created by looking at the following sources: |
105 | |
106 | =over 4 |
107 | |
108 | =item * Content-type header |
109 | |
110 | If this exists, this will always be the first type in the list. |
111 | |
112 | =item * content-type parameter |
113 | |
114 | If the request is a GET request and there is a "content-type" |
115 | parameter in the query string, this will come before any types in the |
116 | Accept header. |
117 | |
118 | =item * Accept header |
119 | |
120 | This will be parsed and the types found will be ordered by the |
121 | relative quality specified for each type. |
122 | |
123 | =back |
124 | |
125 | If a type appears in more than one of these places, it is ordered based on |
126 | where it is first found. |
127 | |
128 | =method preferred_content_type |
129 | |
130 | This returns the first content type found. It is shorthand for: |
131 | |
132 | $request->accepted_content_types->[0] |
133 | |
134 | =method accepts |
135 | |
136 | Given a content type, this returns true if the type is accepted. |
137 | |
138 | Note that this does not do any wildcard expansion of types. |
139 | |
140 | =cut |