X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FAction%2FSerializeBase.pm;h=a4af5930d124700f1fac0d88067e45df3f6c385b;hb=351f252f57354a5a0c50e3c931b0addd4f1988ff;hp=e26152b28b0fdc52f2e346c901336e0c2d1ab2fc;hpb=e601addaf89882fccbc824c1a53328f0d049b32b;p=catagits%2FCatalyst-Action-REST.git diff --git a/lib/Catalyst/Action/SerializeBase.pm b/lib/Catalyst/Action/SerializeBase.pm index e26152b..a4af593 100644 --- a/lib/Catalyst/Action/SerializeBase.pm +++ b/lib/Catalyst/Action/SerializeBase.pm @@ -1,20 +1,23 @@ -# -# Catlyst::Action::SerializeBase.pm -# Created by: Adam Jacob, Marchex, -# -# $Id$ - package Catalyst::Action::SerializeBase; -use strict; -use warnings; +use Moose; +use namespace::autoclean; -use base 'Catalyst::Action'; +extends 'Catalyst::Action'; use Module::Pluggable::Object; -use Data::Dump qw(dump); -use HTTP::Headers::Util qw(split_header_words); +use Catalyst::Request::REST; +use Catalyst::Utils (); + +our $VERSION = '0.98'; +$VERSION = eval $VERSION; -__PACKAGE__->mk_accessors(qw(_serialize_plugins _loaded_plugins)); +after BUILDARGS => sub { + my $class = shift; + my $config = shift; + Catalyst::Request::REST->_insert_self_into( $config->{class} ); +}; + +has [qw(_serialize_plugins _loaded_plugins)] => ( is => 'rw' ); sub _load_content_plugins { my $self = shift; @@ -33,55 +36,54 @@ sub _load_content_plugins { $self->_serialize_plugins( \@plugins ); } - # First, we use the content type in the HTTP Request. It wins all. - my $content_type = $c->request->content_type || ''; - - # Second, we allow GET requests to tunnel the content-type as - # a query param. - if (!$content_type && $c->req->method eq "GET" && $c->req->param('content-type')) { - - $content_type = $c->req->param('content-type'); - } - - # Third, we parse the Accept header, and see if the client - # takes a format we understand. - # - # This is taken from chansen's Apache2::UploadProgress. - my $used_accept = 0; - if ( !$content_type && $c->request->header('Accept') ) { - my $accept_header = $c->request->header('Accept'); - my %accept = (); - my $counter = 0; - - foreach my $pair ( split_header_words($accept_header) ) { - - my ( $type, $qvalue ) = @{$pair}[ 0, 3 ]; - - unless ( defined $qvalue ) { - $qvalue = 1 - ( ++$counter / 1000 ); - } - - $accept{$type} = sprintf( '%.3f', $qvalue ); - } - - foreach my $type ( sort { $accept{$b} <=> $accept{$a} } keys %accept ) - { - - if ( exists $controller->config->{'serialize'}->{'map'}->{$type} ) - { - $content_type = $type; - last; - } - } - $used_accept = 1; - } - # Finally, we load the class. If you have a default serializer, # and we still don't have a content-type that exists in the map, # we'll use it. my $sclass = $search_path . "::"; my $sarg; - my $map = $controller->config->{'serialize'}->{'map'}; + my $map; + + my $config; + + if ( exists $controller->{'serialize'} ) { + $c->log->info("Catalyst::Action::REST - deprecated use of 'serialize' for configuration."); + $c->log->info("Please see 'CONFIGURATION' in Catalyst::Controller::REST."); + $config = $controller->{'serialize'}; + # if they're using the deprecated config, they may be expecting a + # default mapping too. + $config->{map} ||= $controller->{map}; + } else { + $config = $controller; + } + $map = $config->{'map'}; + + # pick preferred content type + my @accepted_types; # priority order, best first + # give top priority to content type specified by stash, if any + my $content_type_stash_key = $config->{content_type_stash_key}; + if ($content_type_stash_key + and my $stashed = $c->stash->{$content_type_stash_key} + ) { + # convert to array if not already a ref + $stashed = [ $stashed ] if not ref $stashed; + push @accepted_types, @$stashed; + } + # then content types requested by caller + push @accepted_types, @{ $c->request->accepted_content_types }; + # then the default + push @accepted_types, $config->{'default'} if $config->{'default'}; + # pick the best match that we have a serializer mapping for + my ($content_type) = grep { $map->{$_} } @accepted_types; + + return $self->_unsupported_media_type($c, $content_type) + if not $content_type; + + # carp about old text/x-json + if ($content_type eq 'text/x-json') { + $c->log->info('Using deprecated text/x-json content-type.'); + $c->log->info('Use application/json instead!'); + } + if ( exists( $map->{$content_type} ) ) { my $mc; if ( ref( $map->{$content_type} ) eq "ARRAY" ) { @@ -102,11 +104,7 @@ sub _load_content_plugins { return $self->_unsupported_media_type($c, $content_type); } } else { - if ( exists( $controller->config->{'serialize'}->{'default'} ) ) { - $sclass .= $controller->config->{'serialize'}->{'default'}; - } else { - return $self->_unsupported_media_type($c, $content_type); - } + return $self->_unsupported_media_type($c, $content_type); } unless ( exists( $self->_loaded_plugins->{$sclass} ) ) { my $load_class = $sclass; @@ -115,8 +113,7 @@ sub _load_content_plugins { eval { require $load_class; }; if ($@) { $c->log->error( - "Error loading $sclass for " . $content_type . ": $!" ) - if $c->log->is_debug; + "Error loading $sclass for " . $content_type . ": $!" ); return $self->_unsupported_media_type($c, $content_type); } else { $self->_loaded_plugins->{$sclass} = 1; @@ -126,7 +123,7 @@ sub _load_content_plugins { if ($search_path eq "Catalyst::Action::Serialize") { if ($content_type) { $c->response->header( 'Vary' => 'Content-Type' ); - } elsif ($used_accept) { + } elsif ($c->request->accept_only) { $c->response->header( 'Vary' => 'Accept' ); } $c->response->content_type($content_type); @@ -139,7 +136,7 @@ sub _unsupported_media_type { my ( $self, $c, $content_type ) = @_; $c->res->content_type('text/plain'); $c->res->status(415); - if (defined($content_type)) { + if (defined($content_type) && $content_type ne "") { $c->res->body( "Content-Type " . $content_type . " is not supported.\r\n" ); } else { @@ -158,13 +155,13 @@ sub _serialize_bad_request { return undef; } +__PACKAGE__->meta->make_immutable; + 1; =head1 NAME -B - -Base class for Catalyst::Action::Serialize and Catlayst::Action::Deserialize. +Catalyst::Action::SerializeBase - Base class for Catalyst::Action::Serialize and Catlayst::Action::Deserialize. =head1 DESCRIPTION @@ -176,15 +173,12 @@ code for L and L. L, L, L, -=head1 AUTHOR - -Adam Jacob , with lots of help from mst and jrockway. +=head1 AUTHORS -Marchex, Inc. paid me while I developed this module. (http://www.marchex.com) +See L for authors. =head1 LICENSE You may distribute this code under the same terms as Perl itself. =cut -