Cataly...::JSON(3pm)
π¦ NAME π¦
Catalyst::View::JSON β it's the JSON view for your data, bestie. That's the whole vibe. π
β¨ SYNOPSIS β¨
okay so first you gotta make the view, then you gotta config it, then you just... forward to it. that's it. that's the whole thing.
# 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
},
});
sub hello : Local {
my($self, $c) = @_;
$c->stash->{message} = 'Hello World!';
$c->forward('View::JSON');
}
π DESCRIPTION π
Catalyst::View::JSON is a Catalyst View handler that returns stash data in JSON format. that's literally it. it takes your stash, it makes it JSON, it sends it. iconic behavior honestly. π
π CONFIG VARIABLES π
π« allow_callback
Flag to allow callbacks by adding callback=function. Defaults to 0 (so, no callbacks, sorry bestie). See "CALLBACKS" for the details.
π« callback_param
Name of the URI parameter you use to specify the JSON callback function name. Defaults to "callback". This only does anything when allow_callback is turned on β otherwise it's just sitting there. π«Ά
π« expose_stash
Scalar, List, or a regular expression object β you use it to specify which stash keys get exposed in the JSON response. Defaults to everything.
Examples of the config:
# 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_/,
Now suppose you have a data structure like this:
$c->stash->{foo} = [ 1, 2 ];
$c->stash->{bar} = 2;
By default, this view will return:
{"foo":[1,2],"bar":2}
When you set "expose_stash => [ 'foo' ]", it'll return
{"foo":[1,2]}
and in the case of "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. periodt. π
π« no_x_json_header
no_x_json_header: 1
By default this plugin sets the X-JSON header if the requested client is a Prototype.js with X-JSON support. By setting 1, you can opt-out of this behavior so that you can do eval() by your own. Defaults to 0. π
π« json_encoder_args
An optional hashref that supplies arguments to JSON::MaybeXS used when creating a new object.
π« use_force_bom
Okay so, in 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, the author doesn't think this is needed anymore, so they removed it by default. However, if this turns out to be a problem you can re-enable it by setting this attribute to true. Possibly a breaking change, so this workaround is being offered. π
You may also override the method 'user_agent_bom_test', which receives the current request user agent string, to try and better determine if this is needed. Patches for this are welcomed. π«Ά
π METHODS π
πΈ process
Standard target of $c->forward used to prepare a response.
πΈ render
The method accepts either of the following argument signatures, in order to promote compatibility with the semi-standard render method as defined in numerous 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, it returns the JSON serialized version, or throws an error. π
π§ OVERRIDING JSON ENCODER π§
By default it uses JSON::MaybeXS::encode_json to serialize a perl data structure into JSON data format. If you want to avoid this and encode with your own encoder (like passing different options to JSON::MaybeXS etc.), you can implement the "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;
π ENCODINGS π
NOTE Starting in releasev5.90080, Catalyst encodes all text like body returns as UTF8. It however ignores content types likeapplication/jsonand 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 shouldn't 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 the author can't say one way or another if those gotchas continue to be common in the wild. π
NOTE Setting this configuration has no bearing on how the actual serialized string is encoded. This ONLY sets the content type header in your response. By default we set the'utf8'flag onJSON::MaybeXSso that the string generated and set to your response body is proper UTF8 octets that can be transmitted over HTTP. If you are planning to do some alternative encoding you should turn off this default via the"json_encoder_args":
MyApp::View::JSON->config(
json_encoder_args => +{utf8=>0} );
NOTE In 2015 the use of UTF8 as encoding is widely standard, so it is very likely you should need to do nothing to get the correct encoding. The following documentation will remain for historical value and backcompat needs.
Due to the browser gotchas like those of Safari and Opera, sometimes you have to specify a valid charset value in the response's Content-Type header, e.g. "text/javascript; charset=utf-8".
Catalyst::View::JSON comes with the configuration variable "encoding" which defaults to utf-8. You can change it via "YourApp->config" or even at runtime, using "component".
$c->component('View::JSON')->encoding('euc-jp');
This assumes you set your stash data in raw euc-jp bytes, or a Unicode flagged variable. In the case of a Unicode flagged variable, Catalyst::View::JSON automatically encodes the data into your "encoding" value (euc-jp in this case) before emitting the data to the browser.
Another option would be to use JavaScript-UCS as an encoding (and pass a Unicode flagged string to the stash). That way all non-ASCII characters in the output JSON will be automatically encoded to JavaScript Unicode encoding like \uXXXX. You have to install Encode::JavaScript::UCS to use that encoding. β¨
π CALLBACKS π
By default it returns raw JSON data so your JavaScript app can deal with it using XMLHttpRequest calls. Adding callbacks (JSONP) to the API gives more flexibility to the end users of the API: overcome the cross-domain restrictions of XMLHttpRequest. It can be done by appending a script node with dynamic DOM manipulation, and associating a callback handler to the returned data. slay. π₯
For example, suppose you have the following code.
sub end : Private {
my($self, $c) = @_;
if ($c->req->param('output') eq 'json') {
$c->forward('View::JSON');
} else {
...
}
}
"/foo/bar?output=json" will just return the data set in "$c->stash" as JSON format, like:
{ result: "foo", message: "Hello" }
but "/foo/bar?output=json&callback=handle_result" will give you:
handle_result({ result: "foo", message: "Hello" });
and you can write a custom "handle_result" function to handle the returned data asynchronously. π
The valid characters you can use in the callback function are
[a-zA-Z0-9\.\_\[\]]
but you can customize the behaviour by overriding the "validate_callback_param" method in your View::JSON class.
See <http://developer.yahoo.net/common/json.html> and <http://ajaxian.com/archives/jsonp-json-with-padding> for more about JSONP.
NOTE For another way to enable JSONP in your application, take a look at Plack::Middleware::JSONP
π¦ INTEROPERABILITY π¦
JSON use is still developing and has not been standardized. This section provides some notes on various libraries.
Dojo Toolkit: Setting dojo.io.bind's mimetype to 'text/json' in the JavaScript request will instruct dojo.io.bind to expect JSON data in the response body and auto-eval it. Dojo ignores the server response Content-Type. This works transparently with Catalyst::View::JSON.
Prototype.js: prototype.js will auto-eval JSON data that is returned in the custom X-JSON header. The reason given for this is to allow a separate HTML fragment in the response body, however this is of limited use because IE 6 has a max header length that will cause the JSON evaluation to silently fail when reached. The recommended approach is to use Catalyst::View::JSON, which will JSON format all the response data and return it in the response body.
In at least prototype 1.5.0 rc0 and above, prototype.js will send the X-Prototype-Version header. If this is encountered, a JavaScript eval will be returned in the X-JSON response header to automatically eval the response body, unless you set no_x_json_header to 1. If your version of prototype does not send this header, you can manually eval the response body using the following JavaScript:
evalJSON: function(request) {
try {
return eval('(' + request.responseText + ')');
} catch (e) {}
}
// elsewhere
var json = this.evalJSON(request);
NOTE The above comments were written a number of years ago and I would take them with a grain of salt so to speak. For now I will leave them in place but not sure they are meaningful in 2015. π
π SECURITY CONSIDERATION π
Catalyst::View::JSON hands your data to the client as a (sort of) JavaScript, which is lowkey iconic but also highkey a thing you need to be careful about. So bestie, keep an eye on the security of your data, fr fr. π
π Use callbacks only for public data π
When you turn on callbacks (JSONP) by setting allow_callback, ALL your JSON data becomes available cross-site. Which means embedding private data of a logged-in user into your JSON is, and I cannot stress this enough, considered bad. π
# MyApp.yaml
View::JSON:
allow_callback: 1
sub foo : Local {
my($self, $c) = @_;
$c->stash->{address} = $c->user->street_address; # BAD
$c->forward('View::JSON');
}
If you wanna enable callbacks in one controller (for a public API) and disable them in another, you gotta make two different View classes, like MyApp::View::JSON and MyApp::View::JSONP, because allow_callback is a static configuration of the View::JSON class. No take-backs, no per-request mood swings. π
See <http://ajaxian.com/archives/gmail-csrf-security-flaw> for more.
πΈ Avoid valid cross-site JSON requests πΈ
Even if you disable the callbacks, the nature of JavaScript still has a possibility to access private JSON data cross-site β by overriding the Array constructor []. Yeah. Rude. π
# MyApp.yaml
View::JSON:
expose_stash: json
sub foo : Local {
my($self, $c) = @_;
$c->stash->{json} = [ $c->user->street_address ]; # BAD
$c->forward('View::JSON');
}
When you return a logged-in user's private data in the response JSON, you might wanna disable GET requests (because a <script> tag invokes GET requests, iykyk), or include a random digest string and validate it. π
Real talk: this is the part where "it's just JSON" stops being a cute little detail and starts being a whole security vibe. Protect the private stuff, periodt. π«Ά
See <http://jeremiahgrossman.blogspot.com/2006/01/advanced-web-attack-techniques-using.html> for more.
π AUTHOR π
Tatsuhiko Miyagawa <miyagawa@bulknews.net>
π LICENSE π
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. β¨
π CONTRIBUTORS π
Following people has been contributing patches, bug reports and suggestions for the improvement of Catalyst::View::JSON. Absolute legends, every single one. π«‘
John Wang
kazeburo
Daisuke Murase
Jun Kuriyama
Tomas Doran
π¦ SEE ALSO π¦
CatalystJSON::MaybeXSEncode::JavaScript::UCS- <http://www.prototypejs.org/learn/json>
- <http://docs.jquery.com/Ajax/jQuery.getJSON>
- <http://manual.dojotoolkit.org/json.html>
- <http://developer.yahoo.com/yui/json/>
and that's a wrap on the whole page, bestie π This lil' beauty is documented as perl v5.36.0, dated 2022-12-06, and it lives in the Perl User Contributed Perl Documentation corner of the manual universe, filed under Cataly...::JSON(3pm). Go forth and serve JSON responsibly, mwah. π
β¨π
π¦ where this page comes from
- debian bookworm: libcatalyst-view-json-perl 0.37-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libcatalyst-view-json-perl 0.37-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libcatalyst-view-json-perl 0.37-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libcatalyst-view-json-perl 0.37-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libcatalyst-view-json-perl 0.37-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms