1Catalyst::Controller::RUEsSeTr(3C)ontributed Perl DocumeCnattaatliyosnt::Controller::REST(3)
2
3
4
6 Catalyst::Controller::REST - A RESTful controller
7
9 package Foo::Controller::Bar;
10 use Moose;
11 use namespace::autoclean;
12
13 BEGIN { extends 'Catalyst::Controller::REST' }
14
15 sub thing : Local : ActionClass('REST') { }
16
17 # Answer GET requests to "thing"
18 sub thing_GET {
19 my ( $self, $c ) = @_;
20
21 # Return a 200 OK, with the data in entity
22 # serialized in the body
23 $self->status_ok(
24 $c,
25 entity => {
26 some => 'data',
27 foo => 'is real bar-y',
28 },
29 );
30 }
31
32 # Answer PUT requests to "thing"
33 sub thing_PUT {
34 my ( $self, $c ) = @_;
35
36 $radiohead = $c->req->data->{radiohead};
37
38 $self->status_created(
39 $c,
40 location => $c->req->uri,
41 entity => {
42 radiohead => $radiohead,
43 }
44 );
45 }
46
48 Catalyst::Controller::REST implements a mechanism for building RESTful
49 services in Catalyst. It does this by extending the normal Catalyst
50 dispatch mechanism to allow for different subroutines to be called
51 based on the HTTP Method requested, while also transparently handling
52 all the serialization/deserialization for you.
53
54 This is probably best served by an example. In the above controller,
55 we have declared a Local Catalyst action on "sub thing", and have used
56 the ActionClass('REST').
57
58 Below, we have declared "thing_GET" and "thing_PUT". Any GET requests
59 to thing will be dispatched to "thing_GET", while any PUT requests will
60 be dispatched to "thing_PUT".
61
62 Any unimplemented HTTP methods will be met with a "405 Method Not
63 Allowed" response, automatically containing the proper list of
64 available methods. You can override this behavior through implementing
65 a custom "thing_not_implemented" method.
66
67 If you do not provide an OPTIONS handler, we will respond to any
68 OPTIONS requests with a "200 OK", populating the Allowed header
69 automatically.
70
71 Any data included in "$c->stash->{'rest'}" will be serialized for you.
72 The serialization format will be selected based on the content-type of
73 the incoming request. It is probably easier to use the "STATUS
74 HELPERS", which are described below.
75
76 "The HTTP POST, PUT, and OPTIONS methods will all automatically
77 deserialize the contents of "$c->request->body" into the
78 "$c->request->data" hashref", based on the request's "Content-type"
79 header. A list of understood serialization formats is below.
80
81 If we do not have (or cannot run) a serializer for a given content-
82 type, a 415 "Unsupported Media Type" error is generated.
83
84 To make your Controller RESTful, simply have it
85
86 BEGIN { extends 'Catalyst::Controller::REST' }
87
89 See "CONFIGURATION" in Catalyst::Action::Serialize. Note that the
90 "serialize" key has been deprecated.
91
93 Catalyst::Controller::REST will automatically serialize your responses,
94 and deserialize any POST, PUT or OPTIONS requests. It evaluates which
95 serializer to use by mapping a content-type to a Serialization module.
96 We select the content-type based on:
97
98 The Content-Type Header
99 If the incoming HTTP Request had a Content-Type header set, we will
100 use it.
101
102 The content-type Query Parameter
103 If this is a GET request, you can supply a content-type query
104 parameter.
105
106 Evaluating the Accept Header
107 Finally, if the client provided an Accept header, we will evaluate
108 it and use the best-ranked choice.
109
111 A given serialization mechanism is only available if you have the
112 underlying modules installed. For example, you can't use XML::Simple
113 if it's not already installed.
114
115 In addition, each serializer has its quirks in terms of what sorts of
116 data structures it will properly handle. Catalyst::Controller::REST
117 makes no attempt to save you from yourself in this regard. :)
118
119 · "text/x-yaml" => "YAML::Syck"
120
121 Returns YAML generated by YAML::Syck.
122
123 · "text/html" => "YAML::HTML"
124
125 This uses YAML::Syck and URI::Find to generate YAML with all URLs
126 turned to hyperlinks. Only usable for Serialization.
127
128 · "application/json" => "JSON"
129
130 Uses JSON to generate JSON output. It is strongly advised to also
131 have JSON::XS installed. The "text/x-json" content type is supported
132 but is deprecated and you will receive warnings in your log.
133
134 You can also add a hash in your controller config to pass options to
135 the json object. There are two options. "json_options" are used when
136 decoding incoming JSON, and "json_options_encode" is used when
137 encoding JSON for output.
138
139 For instance, to relax permissions when deserializing input, add:
140
141 __PACKAGE__->config(
142 json_options => { relaxed => 1 }
143 )
144
145 To indent the JSON output so it becomes more human readable, add:
146
147 __PACKAGE__->config(
148 json_options_encode => { indent => 1 }
149 )
150
151 · "text/javascript" => "JSONP"
152
153 If a callback=? parameter is passed, this returns javascript in the
154 form of: $callback($serializedJSON);
155
156 Note - this is disabled by default as it can be a security risk if
157 you are unaware.
158
159 The usual MIME types for this serialization format are:
160 'text/javascript', 'application/x-javascript',
161 'application/javascript'.
162
163 · "text/x-data-dumper" => "Data::Serializer"
164
165 Uses the Data::Serializer module to generate Data::Dumper output.
166
167 · "text/x-data-denter" => "Data::Serializer"
168
169 Uses the Data::Serializer module to generate Data::Denter output.
170
171 · "text/x-data-taxi" => "Data::Serializer"
172
173 Uses the Data::Serializer module to generate Data::Taxi output.
174
175 · "text/x-config-general" => "Data::Serializer"
176
177 Uses the Data::Serializer module to generate Config::General output.
178
179 · "text/x-php-serialization" => "Data::Serializer"
180
181 Uses the Data::Serializer module to generate PHP::Serialization
182 output.
183
184 · "text/xml" => "XML::Simple"
185
186 Uses XML::Simple to generate XML output. This is probably not
187 suitable for any real heavy XML work. Due to XML::Simples requirement
188 that the data you serialize be a HASHREF, we transform outgoing data
189 to be in the form of:
190
191 { data => $yourdata }
192
193 · View
194
195 Uses a regular Catalyst view. For example, if you wanted to have
196 your "text/html" and "text/xml" views rendered by TT, set:
197
198 __PACKAGE__->config(
199 map => {
200 'text/html' => [ 'View', 'TT' ],
201 'text/xml' => [ 'View', 'XML' ],
202 }
203 );
204
205 Your views should have a "process" method like this:
206
207 sub process {
208 my ( $self, $c, $stash_key ) = @_;
209
210 my $output;
211 eval {
212 $output = $self->serialize( $c->stash->{$stash_key} );
213 };
214 return $@ if $@;
215
216 $c->response->body( $output );
217 return 1; # important
218 }
219
220 sub serialize {
221 my ( $self, $data ) = @_;
222
223 my $serialized = ... process $data here ...
224
225 return $serialized;
226 }
227
228 · Callback
229
230 For infinite flexibility, you can provide a callback for the
231 deserialization/serialization steps.
232
233 __PACKAGE__->config(
234 map => {
235 'text/xml' => [ 'Callback', { deserialize => \&parse_xml, serialize => \&render_xml } ],
236 }
237 );
238
239 The "deserialize" callback is passed a string that is the body of the
240 request and is expected to return a scalar value that results from
241 the deserialization. The "serialize" callback is passed the data
242 structure that needs to be serialized and must return a string
243 suitable for returning in the HTTP response. In addition to
244 receiving the scalar to act on, both callbacks are passed the
245 controller object and the context (i.e. $c) as the second and third
246 arguments.
247
248 By default, Catalyst::Controller::REST will return a "415 Unsupported
249 Media Type" response if an attempt to use an unsupported content-type
250 is made. You can ensure that something is always returned by setting
251 the "default" config option:
252
253 __PACKAGE__->config(default => 'text/x-yaml');
254
255 would make it always fall back to the serializer plugin defined for
256 "text/x-yaml".
257
259 Implementing new Serialization formats is easy! Contributions are most
260 welcome! If you would like to implement a custom serializer, you
261 should create two new modules in the Catalyst::Action::Serialize and
262 Catalyst::Action::Deserialize namespace. Then assign your new class to
263 the content-type's you want, and you're done.
264
265 See Catalyst::Action::Serialize and Catalyst::Action::Deserialize for
266 more information.
267
269 Since so much of REST is in using HTTP, we provide these Status
270 Helpers. Using them will ensure that you are responding with the
271 proper codes, headers, and entities.
272
273 These helpers try and conform to the HTTP 1.1 Specification. You can
274 refer to it at: <http://www.w3.org/Protocols/rfc2616/rfc2616.txt>.
275 These routines are all implemented as regular subroutines, and as such
276 require you pass the current context ($c) as the first argument.
277
278 status_ok
279 Returns a "200 OK" response. Takes an "entity" to serialize.
280
281 Example:
282
283 $self->status_ok(
284 $c,
285 entity => {
286 radiohead => "Is a good band!",
287 }
288 );
289
290 status_created
291 Returns a "201 CREATED" response. Takes an "entity" to serialize,
292 and a "location" where the created object can be found.
293
294 Example:
295
296 $self->status_created(
297 $c,
298 location => $c->req->uri,
299 entity => {
300 radiohead => "Is a good band!",
301 }
302 );
303
304 In the above example, we use the requested URI as our location.
305 This is probably what you want for most PUT requests.
306
307 status_accepted
308 Returns a "202 ACCEPTED" response. Takes an "entity" to serialize.
309 Also takes optional "location" for queue type scenarios.
310
311 Example:
312
313 $self->status_accepted(
314 $c,
315 location => $c->req->uri,
316 entity => {
317 status => "queued",
318 }
319 );
320
321 status_no_content
322 Returns a "204 NO CONTENT" response.
323
324 status_multiple_choices
325 Returns a "300 MULTIPLE CHOICES" response. Takes an "entity" to
326 serialize, which should provide list of possible locations. Also
327 takes optional "location" for preferred choice.
328
329 status_found
330 Returns a "302 FOUND" response. Takes an "entity" to serialize.
331 Also takes optional "location".
332
333 status_bad_request
334 Returns a "400 BAD REQUEST" response. Takes a "message" argument
335 as a scalar, which will become the value of "error" in the
336 serialized response.
337
338 Example:
339
340 $self->status_bad_request(
341 $c,
342 message => "Cannot do what you have asked!",
343 );
344
345 status_forbidden
346 Returns a "403 FORBIDDEN" response. Takes a "message" argument as
347 a scalar, which will become the value of "error" in the serialized
348 response.
349
350 Example:
351
352 $self->status_forbidden(
353 $c,
354 message => "access denied",
355 );
356
357 status_not_found
358 Returns a "404 NOT FOUND" response. Takes a "message" argument as
359 a scalar, which will become the value of "error" in the serialized
360 response.
361
362 Example:
363
364 $self->status_not_found(
365 $c,
366 message => "Cannot find what you were looking for!",
367 );
368
369 gone
370 Returns a "41O GONE" response. Takes a "message" argument as a
371 scalar, which will become the value of "error" in the serialized
372 response.
373
374 Example:
375
376 $self->status_gone(
377 $c,
378 message => "The document have been deleted by foo",
379 );
380
381 status_see_other
382 Returns a "303 See Other" response. Takes an optional "entity" to
383 serialize, and a "location" where the client should redirect to.
384
385 Example:
386
387 $self->status_see_other(
388 $c,
389 location => $some_other_url,
390 entity => {
391 radiohead => "Is a good band!",
392 }
393 );
394
395 status_moved
396 Returns a "301 MOVED" response. Takes an "entity" to serialize,
397 and a "location" where the created object can be found.
398
399 Example:
400
401 $self->status_moved(
402 $c,
403 location => '/somewhere/else',
404 entity => {
405 radiohead => "Is a good band!",
406 },
407 );
408
410 If you want to construct your responses yourself, all you need to do is
411 put the object you want serialized in $c->stash->{'rest'}.
412
414 This Controller ties together Catalyst::Action::REST,
415 Catalyst::Action::Serialize and Catalyst::Action::Deserialize. It
416 should be suitable for most applications. You should be aware that it:
417
418 Configures the Serialization Actions
419 This class provides a default configuration for Serialization. It
420 is currently:
421
422 __PACKAGE__->config(
423 'stash_key' => 'rest',
424 'map' => {
425 'text/html' => 'YAML::HTML',
426 'text/xml' => 'XML::Simple',
427 'text/x-yaml' => 'YAML',
428 'application/json' => 'JSON',
429 'text/x-json' => 'JSON',
430 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
431 'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ],
432 'text/x-data-taxi' => [ 'Data::Serializer', 'Data::Taxi' ],
433 'application/x-storable' => [ 'Data::Serializer', 'Storable' ],
434 'application/x-freezethaw' => [ 'Data::Serializer', 'FreezeThaw' ],
435 'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ],
436 'text/x-php-serialization' => [ 'Data::Serializer', 'PHP::Serialization' ],
437 },
438 );
439
440 You can read the full set of options for this configuration block
441 in Catalyst::Action::Serialize.
442
443 Sets a "begin" and "end" method for you
444 The "begin" method uses Catalyst::Action::Deserialize. The "end"
445 method uses Catalyst::Action::Serialize. If you want to override
446 either behavior, simply implement your own "begin" and "end"
447 actions and forward to another action with the Serialize and/or
448 Deserialize action classes:
449
450 package Foo::Controller::Monkey;
451 use Moose;
452 use namespace::autoclean;
453
454 BEGIN { extends 'Catalyst::Controller::REST' }
455
456 sub begin : Private {
457 my ($self, $c) = @_;
458 ... do things before Deserializing ...
459 $c->forward('deserialize');
460 ... do things after Deserializing ...
461 }
462
463 sub deserialize : ActionClass('Deserialize') {}
464
465 sub end :Private {
466 my ($self, $c) = @_;
467 ... do things before Serializing ...
468 $c->forward('serialize');
469 ... do things after Serializing ...
470 }
471
472 sub serialize : ActionClass('Serialize') {}
473
474 If you need to deserialize multipart requests (i.e. REST data in
475 one part and file uploads in others) you can do so by using the
476 Catalyst::Action::DeserializeMultiPart action class.
477
479 I have code in production using Catalyst::Controller::REST. That said,
480 it is still under development, and it's possible that things may change
481 between releases. I promise to not break things unnecessarily. :)
482
484 Catalyst::Action::REST, Catalyst::Action::Serialize,
485 Catalyst::Action::Deserialize
486
487 For help with REST in general:
488
489 The HTTP 1.1 Spec is required reading.
490 http://www.w3.org/Protocols/rfc2616/rfc2616.txt
491
492 Wikipedia! http://en.wikipedia.org/wiki/Representational_State_Transfer
493
494 The REST Wiki: http://rest.blueoxen.net/cgi-bin/wiki.pl?FrontPage
495
497 See Catalyst::Action::REST for authors.
498
500 You may distribute this code under the same terms as Perl itself.
501
502
503
504perl v5.28.0 2017-12-05 Catalyst::Controller::REST(3)