Commit | Line | Data |
01f9819a |
1 | NAME |
2 | Catalyst::Controller::REST - A RESTful controller |
3 | |
4 | SYNOPSIS |
5 | package Foo::Controller::Bar; |
6 | |
7 | use base 'Catalyst::Controller::REST'; |
8 | |
9 | sub thing : Local : ActionClass('REST') { } |
10 | |
11 | # Answer GET requests to "thing" |
12 | sub thing_GET { |
13 | my ( $self, $c ) = @_; |
14 | |
15 | # Return a 200 OK, with the data in entity |
16 | # serialized in the body |
17 | $self->status_ok( |
18 | $c, |
19 | entity => { |
20 | some => 'data', |
21 | foo => 'is real bar-y', |
22 | }, |
23 | ); |
24 | } |
25 | |
26 | # Answer PUT requests to "thing" |
27 | sub thing_PUT { |
28 | .. some action .. |
29 | } |
30 | |
31 | DESCRIPTION |
32 | Catalyst::Controller::REST implements a mechanism for building RESTful |
33 | services in Catalyst. It does this by extending the normal Catalyst |
34 | dispatch mechanism to allow for different subroutines to be called based |
35 | on the HTTP Method requested, while also transparently handling all the |
36 | serialization/deserialization for you. |
37 | |
38 | This is probably best served by an example. In the above controller, we |
39 | have declared a Local Catalyst action on "sub thing", and have used the |
40 | ActionClass('REST'). |
41 | |
42 | Below, we have declared "thing_GET" and "thing_PUT". Any GET requests to |
43 | thing will be dispatched to "thing_GET", while any PUT requests will be |
44 | dispatched to "thing_PUT". |
45 | |
8f00a41b |
46 | Any unimplemented HTTP methods will be met with a "405 Method Not |
01f9819a |
47 | Allowed" response, automatically containing the proper list of available |
8f00a41b |
48 | methods. You can override this behavior through implementing a custom |
49 | "thing_not_implemented" method. |
50 | |
51 | If you do not provide an OPTIONS handler, we will respond to any OPTIONS |
52 | requests with a "200 OK", populating the Allowed header automatically. |
53 | |
54 | Any data included in "$c->stash->{'rest'}" will be serialized for you. |
55 | The serialization format will be selected based on the content-type of |
56 | the incoming request. It is probably easier to use the "STATUS HELPERS", |
57 | which are described below. |
01f9819a |
58 | |
59 | The HTTP POST, PUT, and OPTIONS methods will all automatically |
60 | deserialize the contents of $c->request->body based on the requests |
61 | content-type header. A list of understood serialization formats is |
62 | below. |
63 | |
8f00a41b |
64 | If we do not have (or cannot run) a serializer for a given content-type, |
65 | a 415 "Unsupported Media Type" error is generated. |
01f9819a |
66 | |
67 | To make your Controller RESTful, simply have it |
68 | |
69 | use base 'Catalyst::Controller::REST'; |
70 | |
71 | SERIALIZATION |
8f00a41b |
72 | Catalyst::Controller::REST will automatically serialize your responses, |
73 | and deserialize any POST, PUT or OPTIONS requests. It evaluates which |
74 | serializer to use by mapping a content-type to a Serialization module. |
75 | We select the content-type based on: |
76 | |
77 | The Content-Type Header |
78 | If the incoming HTTP Request had a Content-Type header set, we will |
79 | use it. |
80 | |
81 | The content-type Query Parameter |
82 | If this is a GET request, you can supply a content-type query |
83 | parameter. |
84 | |
85 | Evaluating the Accept Header |
86 | Finally, if the client provided an Accept header, we will evaluate it |
87 | and use the best-ranked choice. |
88 | |
89 | AVAILABLE SERIALIZERS |
90 | A given serialization mechanism is only available if you have the |
91 | underlying modules installed. For example, you can't use XML::Simple if |
92 | it's not already installed. |
93 | |
94 | In addition, each serializer has it's quirks in terms of what sorts of |
95 | data structures it will properly handle. Catalyst::Controller::REST |
96 | makes no attempt to svae you from yourself in this regard. :) |
97 | |
98 | "text/x-yaml" => "YAML::Syck" |
99 | Returns YAML generated by YAML::Syck. |
100 | |
101 | "text/html" => "YAML::HTML" |
102 | This uses YAML::Syck and URI::Find to generate YAML with all URLs |
103 | turned to hyperlinks. Only useable for Serialization. |
104 | |
105 | "text/x-json" => "JSON::Syck" |
106 | Uses JSON::Syck to generate JSON output |
107 | |
108 | "text/x-data-dumper" => "Data::Serializer" |
109 | Uses the Data::Serializer module to generate Data::Dumper output. |
110 | |
111 | "text/x-data-denter" => "Data::Serializer" |
112 | Uses the Data::Serializer module to generate Data::Denter output. |
113 | |
114 | "text/x-data-taxi" => "Data::Serializer" |
115 | Uses the Data::Serializer module to generate Data::Taxi output. |
116 | |
117 | "application/x-storable" => "Data::Serializer" |
118 | Uses the Data::Serializer module to generate Storable output. |
119 | |
120 | "application/x-freezethaw" => "Data::Serializer" |
121 | Uses the Data::Serializer module to generate FreezeThaw output. |
01f9819a |
122 | |
8f00a41b |
123 | "text/x-config-general" => "Data::Serializer" |
124 | Uses the Data::Serializer module to generate Config::General output. |
01f9819a |
125 | |
8f00a41b |
126 | "text/x-php-serialization" => "Data::Serializer" |
127 | Uses the Data::Serializer module to generate PHP::Serialization |
128 | output. |
129 | |
130 | "text/xml" => "XML::Simple" |
131 | Uses XML::Simple to generate XML output. This is probably not suitable |
132 | for any real heavy XML work. Due to XML::Simples requirement that the |
133 | data you serialize be a HASHREF, we transform outgoing data to be in |
134 | the form of: |
135 | |
136 | { data => $yourdata } |
137 | |
138 | By default, Catalyst::Controller::REST will return a "415 Unsupported |
139 | Media Type" response if an attempt to use an unsupported content-type is |
140 | made. You can ensure that something is always returned by setting the |
141 | "default" config option: |
142 | |
143 | __PACKAGE__->config->{'serialize'}->{'default'} = 'YAML'; |
144 | |
145 | Would make it always fall back to YAML. |
01f9819a |
146 | |
147 | Implementing new Serialization formats is easy! Contributions are most |
148 | welcome! See Catalyst::Action::Serialize and |
149 | Catalyst::Action::Deserialize for more information. |
150 | |
8f00a41b |
151 | CUSTOM SERIALIZERS |
152 | If you would like to implement a custom serializer, you should create |
153 | two new modules in the Catalyst::Action::Serialize and |
154 | Catalyst::Action::Deserialize namespace. Then assign your new class to |
155 | the content-type's you want, and you're done. |
156 | |
01f9819a |
157 | STATUS HELPERS |
8f00a41b |
158 | Since so much of REST is in using HTTP, we provide these Status Helpers. |
159 | Using them will ensure that you are responding with the proper codes, |
160 | headers, and entities. |
161 | |
01f9819a |
162 | These helpers try and conform to the HTTP 1.1 Specification. You can |
8f00a41b |
163 | refer to it at: <http://www.w3.org/Protocols/rfc2616/rfc2616.txt>. These |
01f9819a |
164 | routines are all implemented as regular subroutines, and as such require |
165 | you pass the current context ($c) as the first argument. |
166 | |
167 | status_ok |
168 | Returns a "200 OK" response. Takes an "entity" to serialize. |
169 | |
170 | Example: |
171 | |
172 | $self->status_ok( |
173 | $c, |
174 | entity => { |
175 | radiohead => "Is a good band!", |
176 | } |
177 | ); |
178 | |
179 | status_created |
180 | Returns a "201 CREATED" response. Takes an "entity" to serialize, |
181 | and a "location" where the created object can be found. |
182 | |
183 | Example: |
184 | |
185 | $self->status_created( |
186 | $c, |
187 | location => $c->req->uri->as_string, |
188 | entity => { |
189 | radiohead => "Is a good band!", |
190 | } |
191 | ); |
192 | |
193 | In the above example, we use the requested URI as our location. This |
194 | is probably what you want for most PUT requests. |
195 | |
196 | status_accepted |
197 | Returns a "202 ACCEPTED" response. Takes an "entity" to serialize. |
198 | |
199 | Example: |
200 | |
201 | $self->status_accepted( |
202 | $c, |
203 | entity => { |
204 | status => "queued", |
205 | } |
206 | ); |
207 | |
208 | status_bad_request |
209 | Returns a "400 BAD REQUEST" response. Takes a "message" argument as |
210 | a scalar, which will become the value of "error" in the serialized |
211 | response. |
212 | |
213 | Example: |
214 | |
215 | $self->status_bad_request( |
216 | $c, |
8f00a41b |
217 | message => "Cannot do what you have asked!", |
01f9819a |
218 | ); |
219 | |
220 | status_not_found |
221 | Returns a "404 NOT FOUND" response. Takes a "message" argument as a |
222 | scalar, which will become the value of "error" in the serialized |
223 | response. |
224 | |
225 | Example: |
226 | |
227 | $self->status_not_found( |
228 | $c, |
8f00a41b |
229 | message => "Cannot find what you were looking for!", |
01f9819a |
230 | ); |
231 | |
232 | MANUAL RESPONSES |
233 | If you want to construct your responses yourself, all you need to do is |
234 | put the object you want serialized in $c->stash->{'rest'}. |
235 | |
8f00a41b |
236 | IMPLEMENTATION DETAILS |
237 | This Controller ties together Catalyst::Action::REST, |
238 | Catalyst::Action::Serialize and Catalyst::Action::Deserialize. It should |
239 | be suitable for most applications. You should be aware that it: |
240 | |
241 | Configures the Serialization Actions |
242 | This class provides a default configuration for Serialization. It is |
243 | currently: |
244 | |
245 | __PACKAGE__->config( |
246 | serialize => { |
247 | 'stash_key' => 'rest', |
248 | 'map' => { |
249 | 'text/html' => 'YAML::HTML', |
250 | 'text/xml' => 'XML::Simple', |
251 | 'text/x-yaml' => 'YAML', |
252 | 'text/x-json' => 'JSON', |
253 | 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ], |
254 | 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ], |
255 | 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ], |
256 | 'application/x-storable' => [ 'Data::Serializer', 'Storable' |
257 | ], |
258 | 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' |
259 | ], |
260 | 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ] |
261 | , |
262 | 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serializat |
263 | ion' ], |
264 | }, |
265 | } |
266 | ); |
267 | |
268 | You can read the full set of options for this configuration block in |
269 | Catalyst::Action::Serialize. |
270 | |
271 | Sets a "begin" and "end" method for you |
272 | The "begin" method uses Catalyst::Action::Deserialize. The "end" |
273 | method uses Catalyst::Action::Serialize. If you want to override |
274 | either behavior, simply implement your own "begin" and "end" actions |
275 | and use NEXT: |
276 | |
277 | my Foo::Controller::Monkey; |
278 | use base qw(Catalyst::Controller::REST); |
279 | |
280 | sub begin :Private { |
281 | my ($self, $c) = @_; |
282 | ... do things before Deserializing ... |
283 | $self->NEXT::begin($c); |
284 | ... do things after Deserializing ... |
285 | } |
286 | |
287 | sub end :Private { |
288 | my ($self, $c) = @_; |
289 | ... do things before Serializing ... |
290 | $self->NEXT::end($c); |
291 | ... do things after Serializing ... |
292 | } |
293 | |
294 | A MILD WARNING |
295 | I have code in production using Catalyst::Controller::REST. That |
296 | said, it is still under development, and it's possible that things |
297 | may change between releases. I promise to not break things |
298 | unneccesarily. :) |
299 | |
01f9819a |
300 | SEE ALSO |
8f00a41b |
301 | Catalyst::Action::REST, Catalyst::Action::Serialize, |
302 | Catalyst::Action::Deserialize |
01f9819a |
303 | |
8f00a41b |
304 | For help with REST in general: |
01f9819a |
305 | |
8f00a41b |
306 | The HTTP 1.1 Spec is required reading. |
307 | http://www.w3.org/Protocols/rfc2616/rfc2616.txt |
01f9819a |
308 | |
8f00a41b |
309 | Wikipedia! |
310 | http://en.wikipedia.org/wiki/Representational_State_Transfer |
01f9819a |
311 | |
8f00a41b |
312 | The REST Wiki: http://rest.blueoxen.net/cgi-bin/wiki.pl?FrontPage |
01f9819a |
313 | |
314 | AUTHOR |
8f00a41b |
315 | Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and |
316 | jrockway |
01f9819a |
317 | |
8f00a41b |
318 | Marchex, Inc. paid me while I developed this module. |
319 | (http://www.marchex.com) |
01f9819a |
320 | |
321 | LICENSE |
8f00a41b |
322 | You may distribute this code under the same terms as Perl itself. |
01f9819a |
323 | |