Catalyst-View-JSON
view release on metacpan or search on metacpan
lib/Catalyst/View/JSON.pm view on Meta::CPAN
$self->json_dumper( sub {
my($data, $self, $c) = @_;
$method->($self, $c, $data);
} );
} else {
require JSON::MaybeXS;
my %args = (utf8=>1, %{$self->json_encoder_args ||+{}});
my $json = JSON::MaybeXS->new(%args);
$self->json_dumper(sub { $json->encode($_[0]) });
}
return $self;
}
sub process {
my($self, $c) = @_;
# get the response data from stash
my $cond = sub { 1 };
my $single_key;
if (my $expose = $self->expose_stash) {
if (ref($expose) eq 'Regexp') {
$cond = sub { $_[0] =~ $expose };
} elsif (ref($expose) eq 'ARRAY') {
my %match = map { $_ => 1 } @$expose;
$cond = sub { $match{$_[0]} };
} elsif (!ref($expose)) {
$single_key = $expose;
} else {
$c->log->warn("expose_stash should be an array reference, Regexp object, or key for a single stash entry.");
$c->log->warn("Returning all stash entries");
}
}
my $data;
if ($single_key) {
$data = $c->stash->{$single_key};
} else {
$data = { map { $cond->($_) ? ($_ => $c->stash->{$_}) : () }
keys %{$c->stash} };
}
my $cb_param = $self->allow_callback
? ($self->callback_param || 'callback') : undef;
my $cb = $cb_param ? $c->req->param($cb_param) : undef;
$self->validate_callback_param($cb) if $cb;
# When you set encoding option in View::JSON, this plugin DWIMs
my $encoding = $self->encoding || 'utf-8';
$c->res->content_type("application/json; charset=$encoding");
if ($c->req->header('X-Prototype-Version') && !$self->no_x_json_header) {
$c->res->header('X-JSON' => 'eval("("+this.transport.responseText+")")');
}
my $json = $self->render($c, $data);
my $output;
## add UTF-8 BOM if the client meets a test and the application wants it.
if ($self->use_force_bom && $encoding eq 'utf-8') {
my $user_agent = $c->req->user_agent || '';
if ($self->user_agent_bom_test($user_agent)) {
$output = "\xEF\xBB\xBF";
}
}
$output .= "$cb(" if $cb;
$output .= $json;
$output .= ");" if $cb;
$c->res->output($output);
}
# allow for called as $c, $template, $data || $c, $data so that we are compatible
# with the semi standard render method that a lot of views use.
sub render {
my $self = shift;
my $c = shift;
my $data = pop;
return $self->json_dumper->($data, $self, $c); # weird order to be backward compat
}
sub user_agent_bom_test {
my ($self, $user_agent) = @_;
return(($user_agent =~ m/\bSafari\b/) and ($user_agent !~ m/\bChrome\b/));
}
sub validate_callback_param {
my($self, $param) = @_;
$param =~ /^[a-zA-Z0-9\.\_\[\]]+$/
or Catalyst::Exception->throw("Invalid callback parameter $param");
}
1;
__END__
=head1 NAME
Catalyst::View::JSON - JSON view for your data
=head1 SYNOPSIS
# lib/MyApp/View/JSON.pm
package MyApp::View::JSON;
use base qw( Catalyst::View::JSON );
1;
# configure in lib/MyApp.pm
MyApp->config({
...
'View::JSON' => {
allow_callback => 1, # defaults to 0
callback_param => 'cb', # defaults to 'callback'
expose_stash => [ qw(foo bar) ], # defaults to everything
},
});
lib/Catalyst/View/JSON.pm view on Meta::CPAN
=item allow_callback
Flag to allow callbacks by adding C<callback=function>. Defaults to 0
(doesn't allow callbacks). See L</CALLBACKS> for details.
=item callback_param
Name of URI parameter to specify JSON callback function name. Defaults
to C<callback>. Only effective when C<allow_callback> is turned on.
=item expose_stash
Scalar, List or regular expression object, to specify which stash keys are
exposed as a JSON response. Defaults to everything. Examples configuration:
# use 'json_data' value as a data to return
expose_stash => 'json_data',
# only exposes keys 'foo' and 'bar'
expose_stash => [ qw( foo bar ) ],
# only exposes keys that matches with /^json_/
expose_stash => qr/^json_/,
Suppose you have data structure of the following.
$c->stash->{foo} = [ 1, 2 ];
$c->stash->{bar} = 2;
By default, this view will return:
{"foo":[1,2],"bar":2}
When you set C<< expose_stash => [ 'foo' ] >>, it'll return
{"foo":[1,2]}
and in the case of C<< expose_stash => 'foo' >>, it'll just return
[1,2]
instead of the whole object (hashref in perl). This option will be
useful when you share the method with different views (e.g. TT) and
don't want to expose non-irrelevant stash variables as in JSON.
=item no_x_json_header
no_x_json_header: 1
By default this plugin sets X-JSON header if the requested client is a
Prototype.js with X-JSON support. By setting 1, you can opt-out this
behavior so that you can do eval() by your own. Defaults to 0.
=item json_encoder_args
An optional hashref that supplies arguments to L<JSON::MaybeXS> used when creating
a new object.
=item use_force_bom
If versions of this view older than 0.36, there was some code that added a UTF-8 BOM
marker to the end of the JSON string when the user agent was Safari. After looking
at a lot of existing code I don't think this is needed anymore so we removed it by
default. However if this turns out to be a problem you can re enable it by setting
this attribute to true. Possible a breaking change so we offer this workaround.
You may also override the method 'user_agent_bom_test' which received the current
request user agent string to try and better determine if this is needed. Patches
for this welcomed.
=back
=head1 METHODS
=head2 process
Standard target of $c->forward used to prepare a response
=head2 render
The methods accepts either of the following argument signatures in order to promote
compatibility with the semi standard render method as define in numerous L<Catalyst>
views on CPAN:
my $json_string = $c->view('JSON')->render($c, undef, $data);
my $json_string = $c->view('JSON')->render($c, $data);
Given '$data' returns the JSON serialized version, or throws and error.
=head1 OVERRIDING JSON ENCODER
By default it uses L<JSON::MaybeXS::encode_json> to serialize perl data structure into
JSON data format. If you want to avoid this and encode with your own
encoder (like passing different options to L<JSON::MaybeXS> etc.), you can implement
the C<encode_json> method in your View class.
package MyApp::View::JSON;
use base qw( Catalyst::View::JSON );
use JSON::MaybeXS ();
sub encode_json {
my($self, $c, $data) = @_;
my $encoder = JSON::MaybeXS->new->(ascii => 1, pretty => 1, allow_nonref => 1);
$encoder->encode($data);
}
1;
=head1 ENCODINGS
B<NOTE> Starting in release v5.90080 L<Catalyst> encodes all text
like body returns as UTF8. It however ignores content types like
application/json and assumes that a correct JSON serializer is
doing what it is supposed to do, which is encode UTF8 automatically.
In general this is what this view does so you shoulding need to
mess with the encoding flag here unless you have some odd case.
Also, the comment about regard 'browser gotcha's' was written a
number of years ago and I can't say one way or another if those
gotchas continue to be common in the wild.
( run in 0.883 second using v1.01-cache-2.11-cpan-39bf76dae61 )