t push origin masterMerge branch 'bockscar-master'
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / Action / Serialize.pm
CommitLineData
7ad87df9 1package Catalyst::Action::Serialize;
2
930013e6 3use Moose;
4use namespace::autoclean;
7ad87df9 5
930013e6 6extends 'Catalyst::Action::SerializeBase';
7ad87df9 7use Module::Pluggable::Object;
def65dcc 8use MRO::Compat;
7ad87df9 9
0c14c8cd 10has _encoders => (
11 is => 'ro',
12 isa => 'HashRef',
13 default => sub { {} },
14);
15
7ad87df9 16sub execute {
17 my $self = shift;
18 my ( $controller, $c ) = @_;
19
def65dcc 20 $self->maybe::next::method(@_);
e601adda 21
7ad87df9 22 return 1 if $c->req->method eq 'HEAD';
786c212f 23 return 1 if $c->response->has_body;
7ad87df9 24 return 1 if scalar @{ $c->error };
bdff70a9 25 return 1 if $c->response->status =~ /^(?:204)$/;
02f66fc0 26 return 1 if defined $c->stash->{current_view};
27 return 1 if defined $c->stash->{current_view_instance};
7ad87df9 28
ffef3497 29 # on 3xx responses, serialize if there's something to
30 # serialize, no-op if not
31 my $stash_key = (
32 $controller->{'serialize'} ?
33 $controller->{'serialize'}->{'stash_key'} :
34 $controller->{'stash_key'}
35 ) || 'rest';
36 return 1 if $c->response->status =~ /^(?:3\d\d)$/ && ! defined $c->stash->{$stash_key};
37
9a76221e 38 my ( $sclass, $sarg, $content_type ) =
39 $self->_load_content_plugins( "Catalyst::Action::Serialize",
40 $controller, $c );
51cc8fe9 41 unless ( defined($sclass) ) {
9a76221e 42 if ( defined($content_type) ) {
faf5c20b 43 $c->log->info("Could not find a serializer for $content_type");
9a76221e 44 } else {
faf5c20b 45 $c->log->info(
9cd203c9 46 "Could not find a serializer for an empty content-type");
9a76221e 47 }
51cc8fe9 48 return 1;
49 }
9a76221e 50 $c->log->debug(
faf5c20b 51 "Serializing with $sclass" . ( $sarg ? " [$sarg]" : '' ) ) if $c->debug;
7ad87df9 52
0c14c8cd 53 $self->_encoders->{$sclass} ||= $sclass->new;
54 my $sobj = $self->_encoders->{$sclass};
55
e601adda 56 my $rc;
878b2b54 57 eval {
58 if ( defined($sarg) ) {
0c14c8cd 59 $rc = $sobj->execute( $controller, $c, $sarg );
878b2b54 60 } else {
0c14c8cd 61 $rc = $sobj->execute( $controller, $c );
878b2b54 62 }
63 };
64 if ($@) {
b3996af8 65 return $self->serialize_bad_request( $c, $content_type, $@ );
878b2b54 66 } elsif (!$rc) {
b3996af8 67 return $self->unsupported_media_type( $c, $content_type );
9a76221e 68 }
7ad87df9 69
70 return 1;
eccb2137 71}
7ad87df9 72
05009b91 73__PACKAGE__->meta->make_immutable;
398c5a1b 74
d2c41c14 751;
76
398c5a1b 77=head1 NAME
78
79Catalyst::Action::Serialize - Serialize Data in a Response
80
81=head1 SYNOPSIS
82
83 package Foo::Controller::Bar;
84
85 __PACKAGE__->config(
faf5c20b 86 'default' => 'text/x-yaml',
87 'stash_key' => 'rest',
88 'map' => {
89 'text/html' => [ 'View', 'TT', ],
90 'text/x-yaml' => 'YAML',
91 'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
398c5a1b 92 }
93 );
94
e601adda 95 sub end :ActionClass('Serialize') {}
398c5a1b 96
97=head1 DESCRIPTION
98
99This action will serialize the body of an HTTP Response. The serializer is
e601adda 100selected by introspecting the HTTP Requests content-type header.
398c5a1b 101
faf5c20b 102It requires that your Catalyst controller is properly configured to set up the
103mapping between Content Type's and Serialization classes.
398c5a1b 104
faf5c20b 105The specifics of serializing each content-type is implemented as a plugin to
106L<Catalyst::Action::Serialize>.
398c5a1b 107
e601adda 108Typically, you would use this ActionClass on your C<end> method. However,
109nothing is stopping you from choosing specific methods to Serialize:
110
111 sub foo :Local :ActionClass('Serialize') {
112 .. populate stash with data ..
113 }
114
9a76221e 115When you use this module, the request class will be changed to
116L<Catalyst::Request::REST>.
117
398c5a1b 118=head1 CONFIGURATION
119
a51e7bbd 120=head2 map
398c5a1b 121
a51e7bbd 122Takes a hashref, mapping Content-Types to a given serializer plugin.
398c5a1b 123
a51e7bbd 124=head2 default
367b3ff4 125
a51e7bbd 126This is the 'fall-back' Content-Type if none of the requested or acceptable
127types is found in the L</map>. It must be an entry in the L</map>.
398c5a1b 128
0c14c8cd 129=head2 stash_key
398c5a1b 130
a51e7bbd 131Specifies the key of the stash entry holding the data that is to be serialized.
132So if the value is "rest", we will serialize the data under:
e601adda 133
134 $c->stash->{'rest'}
398c5a1b 135
a51e7bbd 136=head2 content_type_stash_key
398c5a1b 137
a51e7bbd 138Specifies the key of the stash entry that optionally holds an overriding
139Content-Type. If set, and if the specified stash entry has a valid value,
140then it takes priority over the requested content types.
398c5a1b 141
a51e7bbd 142This can be useful if you want to dynamically force a particular content type,
143perhaps for debugging.
398c5a1b 144
e601adda 145=head1 HELPFUL PEOPLE
146
147Daisuke Maki pointed out that early versions of this Action did not play
148well with others, or generally behave in a way that was very consistent
0c14c8cd 149with the rest of Catalyst.
e601adda 150
b3996af8 151=head1 CUSTOM ERRORS
152
153For building custom error responses when serialization fails, you can create
154an ActionRole (and use L<Catalyst::Controller::ActionRole> to apply it to the
b74200b3 155C<end> action) which overrides C<unsupported_media_type> and/or C<serialize_bad_request>
b3996af8 156methods.
157
398c5a1b 158=head1 SEE ALSO
159
160You likely want to look at L<Catalyst::Controller::REST>, which implements
e601adda 161a sensible set of defaults for doing a REST controller.
398c5a1b 162
163L<Catalyst::Action::Deserialize>, L<Catalyst::Action::REST>
164
5cb5f6bb 165=head1 AUTHORS
398c5a1b 166
5cb5f6bb 167See L<Catalyst::Action::REST> for authors.
398c5a1b 168
169=head1 LICENSE
170
171You may distribute this code under the same terms as Perl itself.
172
173=cut