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 | |
367b3ff4 |
143 | __PACKAGE__->config->{'serialize'}->{'default'} = 'text/x-yaml'; |
8f00a41b |
144 | |
367b3ff4 |
145 | Would make it always fall back to the serializer plugin defined for |
146 | text/x-yaml. |
01f9819a |
147 | |
148 | Implementing new Serialization formats is easy! Contributions are most |
149 | welcome! See Catalyst::Action::Serialize and |
150 | Catalyst::Action::Deserialize for more information. |
151 | |
8f00a41b |
152 | CUSTOM SERIALIZERS |
153 | If you would like to implement a custom serializer, you should create |
154 | two new modules in the Catalyst::Action::Serialize and |
155 | Catalyst::Action::Deserialize namespace. Then assign your new class to |
156 | the content-type's you want, and you're done. |
157 | |
01f9819a |
158 | STATUS HELPERS |
8f00a41b |
159 | Since so much of REST is in using HTTP, we provide these Status Helpers. |
160 | Using them will ensure that you are responding with the proper codes, |
161 | headers, and entities. |
162 | |
01f9819a |
163 | These helpers try and conform to the HTTP 1.1 Specification. You can |
8f00a41b |
164 | refer to it at: <http://www.w3.org/Protocols/rfc2616/rfc2616.txt>. These |
01f9819a |
165 | routines are all implemented as regular subroutines, and as such require |
166 | you pass the current context ($c) as the first argument. |
167 | |
168 | status_ok |
169 | Returns a "200 OK" response. Takes an "entity" to serialize. |
170 | |
171 | Example: |
172 | |
173 | $self->status_ok( |
174 | $c, |
175 | entity => { |
176 | radiohead => "Is a good band!", |
177 | } |
178 | ); |
179 | |
180 | status_created |
181 | Returns a "201 CREATED" response. Takes an "entity" to serialize, |
182 | and a "location" where the created object can be found. |
183 | |
184 | Example: |
185 | |
186 | $self->status_created( |
187 | $c, |
188 | location => $c->req->uri->as_string, |
189 | entity => { |
190 | radiohead => "Is a good band!", |
191 | } |
192 | ); |
193 | |
194 | In the above example, we use the requested URI as our location. This |
195 | is probably what you want for most PUT requests. |
196 | |
197 | status_accepted |
198 | Returns a "202 ACCEPTED" response. Takes an "entity" to serialize. |
199 | |
200 | Example: |
201 | |
202 | $self->status_accepted( |
203 | $c, |
204 | entity => { |
205 | status => "queued", |
206 | } |
207 | ); |
208 | |
209 | status_bad_request |
210 | Returns a "400 BAD REQUEST" response. Takes a "message" argument as |
211 | a scalar, which will become the value of "error" in the serialized |
212 | response. |
213 | |
214 | Example: |
215 | |
216 | $self->status_bad_request( |
217 | $c, |
8f00a41b |
218 | message => "Cannot do what you have asked!", |
01f9819a |
219 | ); |
220 | |
221 | status_not_found |
222 | Returns a "404 NOT FOUND" response. Takes a "message" argument as a |
223 | scalar, which will become the value of "error" in the serialized |
224 | response. |
225 | |
226 | Example: |
227 | |
228 | $self->status_not_found( |
229 | $c, |
8f00a41b |
230 | message => "Cannot find what you were looking for!", |
01f9819a |
231 | ); |
232 | |
233 | MANUAL RESPONSES |
234 | If you want to construct your responses yourself, all you need to do is |
235 | put the object you want serialized in $c->stash->{'rest'}. |
236 | |
8f00a41b |
237 | IMPLEMENTATION DETAILS |
238 | This Controller ties together Catalyst::Action::REST, |
239 | Catalyst::Action::Serialize and Catalyst::Action::Deserialize. It should |
240 | be suitable for most applications. You should be aware that it: |
241 | |
242 | Configures the Serialization Actions |
243 | This class provides a default configuration for Serialization. It is |
244 | currently: |
245 | |
246 | __PACKAGE__->config( |
247 | serialize => { |
248 | 'stash_key' => 'rest', |
249 | 'map' => { |
250 | 'text/html' => 'YAML::HTML', |
251 | 'text/xml' => 'XML::Simple', |
252 | 'text/x-yaml' => 'YAML', |
253 | 'text/x-json' => 'JSON', |
254 | 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ], |
255 | 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ], |
256 | 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ], |
257 | 'application/x-storable' => [ 'Data::Serializer', 'Storable' |
258 | ], |
259 | 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' |
260 | ], |
261 | 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ] |
262 | , |
263 | 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serializat |
264 | ion' ], |
265 | }, |
266 | } |
267 | ); |
268 | |
269 | You can read the full set of options for this configuration block in |
270 | Catalyst::Action::Serialize. |
271 | |
272 | Sets a "begin" and "end" method for you |
273 | The "begin" method uses Catalyst::Action::Deserialize. The "end" |
274 | method uses Catalyst::Action::Serialize. If you want to override |
275 | either behavior, simply implement your own "begin" and "end" actions |
276 | and use NEXT: |
277 | |
278 | my Foo::Controller::Monkey; |
279 | use base qw(Catalyst::Controller::REST); |
280 | |
281 | sub begin :Private { |
282 | my ($self, $c) = @_; |
283 | ... do things before Deserializing ... |
284 | $self->NEXT::begin($c); |
285 | ... do things after Deserializing ... |
286 | } |
287 | |
288 | sub end :Private { |
289 | my ($self, $c) = @_; |
290 | ... do things before Serializing ... |
291 | $self->NEXT::end($c); |
292 | ... do things after Serializing ... |
293 | } |
294 | |
295 | A MILD WARNING |
296 | I have code in production using Catalyst::Controller::REST. That |
297 | said, it is still under development, and it's possible that things |
298 | may change between releases. I promise to not break things |
299 | unneccesarily. :) |
300 | |
01f9819a |
301 | SEE ALSO |
8f00a41b |
302 | Catalyst::Action::REST, Catalyst::Action::Serialize, |
303 | Catalyst::Action::Deserialize |
01f9819a |
304 | |
8f00a41b |
305 | For help with REST in general: |
01f9819a |
306 | |
8f00a41b |
307 | The HTTP 1.1 Spec is required reading. |
308 | http://www.w3.org/Protocols/rfc2616/rfc2616.txt |
01f9819a |
309 | |
8f00a41b |
310 | Wikipedia! |
311 | http://en.wikipedia.org/wiki/Representational_State_Transfer |
01f9819a |
312 | |
8f00a41b |
313 | The REST Wiki: http://rest.blueoxen.net/cgi-bin/wiki.pl?FrontPage |
01f9819a |
314 | |
315 | AUTHOR |
8f00a41b |
316 | Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and |
317 | jrockway |
01f9819a |
318 | |
8f00a41b |
319 | Marchex, Inc. paid me while I developed this module. |
320 | (http://www.marchex.com) |
01f9819a |
321 | |
322 | LICENSE |
8f00a41b |
323 | You may distribute this code under the same terms as Perl itself. |
01f9819a |
324 | |