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