The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Catalyst::View::JSON::PerRequest - JSON View that owns its data

SYNOPSIS

    MyApp->inject_components(
      'View::JSON' => { from_component => 'Catalyst::View::JSON::PerRequest' }
    );

    # In a controller...

    sub root :Chained(/) CaptureArgs(0) {
      my ($self, $c) = @_;
      $c->view('JSON')->data->set(z=>1);
    }

    sub midpoint :Chained(root) CaptureArgs(0) {
      my ($self, $c) = @_;
      $c->view('JSON')->data->set(y=>1);
    }

    sub endpoint :Chained(midpoint) Args(0) {
      my ($self, $c) = @_;
      $c->view('JSON')->created({
        a => 1,
        b => 2,
        c => 3,
      });
    }

DESCRIPTION

This is a Catalyst::View that produces JSON response from a given model. It differs from some of the more classic JSON producing views (such as Catalyst::View::JSON in that is is a per request view (one view for each request) and it defines a 'data' method to hold information to use to produce a view.

It also generates some local response helpers. You may or may not find this approach leads to cleaner code.

METHODS

This view defines the following methods

data (?$model)

Used to set the view data model, and/or to called methods on it (for example to set attributes that will later be used in the JSON response.).

The default is an injected model based on Catalyst::Model::JSON::ViewData which you should review for basic usage. I recommend setting it to a custom model that better encapsulates your view data. You may use any model in your Catalyst application as long as it does the method "TO_JSON".

You may only set the view data model once. If you don't set it and just call methods on it, the default view model is automatically used.

res

response

    $view->response($status, @headers, \%data||$object);
    $view->response($status, \%data||$object);
    $view->response(\%data||$object);
    $view->response($status);
    $view->response($status, @headers);

Used to setup a response. Calling this method will setup an http status, finalize headers and set a body response for the JSON. Content type will be set to 'application/json' automatically (you don't need to set this in a header).

Method '->response' Helpers

We map status codes from HTTP::Status into methods to make sending common request types more simple and more descriptive. The following are the same:

    $c->view->response(200, @args);
    $c->view->ok(@args);

    do { $c->view->response(200, @args); $c->detach };
    $c->view->detach_ok(@args);

See HTTP::Status for a full list of all the status code helpers.

render ($data)

Given a Perl data will return the JSON encoded version.

    my $json = $c->view->render(\%data);

Should be a reference or object that does 'TO_JSON'

process

used as a target for $c->forward. This is mostly here for compatibility with some existing methodology. For example allows using this view with the Renderview action class (common practice). I'd consider it a depracated approach, personally.

ATTRIBUTES

This View defines the following attributes that can be set during configuration

callback_param

Optional. If set, we use this to get a method name for JSONP from the query parameters.

For example if 'callback_param' is 'callback' and the request is:

    localhost/foo/bar?callback=mymethod

Then the JSON response will be wrapped in a function call similar to:

    mymethod({
      'foo': 'bar',
      'baz': 'bin});

Which is a common technique for overcoming some cross-domain restrictions of XMLHttpRequest.

There are some restrictions to the value of the callback method, for security. For more see: http://ajaxian.com/archives/jsonp-json-with-padding

default_view_model

The Catalyst model that is the default model for your JSON return. The default is set to a local instance of Catalyst::Model::JSON::ViewData

json_class

The class used to perform JSON encoding. Default is JSON::MaybeXS

json_init_args

Arguments used to initialize the "json_class". Defaults to:

    our %JSON_INIT_ARGS = (
      utf8 => 1,
      convert_blessed => 1);

json_extra_init_args

Allows you to 'tack on' some arguments to the JSON initialization without messing with the defaults. Unless you really need to override the defaults this is the method you should use.

handle_encode_error

A reference to a subroutine that is called when there is a failure to encode the data given into a JSON format. This can be used globally as an attribute on the defined configuration for the view, and you can set it or overide the global settings on a context basis.

Setting this optional attribute will capture and handle error conditions. We will NOT bubble the error up to the global Catalyst error handling (we don't set $c->error for example). If you want that you need to set it yourself in a custom handler, or don't define one.

The subroutine receives two arguments: the view object and the exception. You must setup a new, valid response. For example:

    package MyApp::View::JSON;

    use Moo;
    extends 'Catalyst::View::JSON::PerRequest';

    package MyApp;

    use Catalyst;

    MyApp->config(
      default_view =>'JSON',
      'View::JSON' => {
        handle_encode_error => sub {
          my ($view, $err) = @_;
          $view->detach_bad_request({ err => "$err"});
        },
      },
    );

    MyApp->setup;

Or setup/override per context:

    sub error :Local Args(0) {
      my ($self, $c) = @_;

      $c->view->handle_encode_error(sub {
          my ($view, $err) = @_;
          $view->detach_bad_request({ err => "$err"});
        });

      $c->view->ok( $bad_data );
    }

NOTE If you mess up the return value (you return something that can't be encoded) a second exception will occur which will NOT be handled and will then bubble up to the main application.

NOTE The view package contains a global function to a usable default error handler, should you wish to use something consistent and reasonably valid. Example:

    MyApp->config(
      default_view =>'JSON',
      'View::JSON' => {
        handle_encode_error => \&Catalyst::View::JSON::PerRequest::HANDLE_ENCODE_ERROR,
      },
    );

The example handler is defined like this:

  sub HANDLE_ENCODE_ERROR {
    my ($view, $err) = @_;
    $view->detach_internal_server_error({ error => "$err"});
  }

UTF-8 NOTES

Generally a view should not do any encoding since the core Catalyst framework handles all this for you. However, historically the popular Catalyst JSON views and related ecosystem (such as Catalyst::Action::REST) have done UTF8 encoding and as a result for compatibility core Catalyst code will assume a response content type of 'application/json' is already UTF8 encoded. So even though this is a new module, we will continue to maintain this historical situation for compatibility reasons. As a result the UTF8 encoding flags will be enabled and expect the contents of $c->res->body to be encoded as expected. If you set your own JSON class for encoding, or set your own initialization arguments, please keep in mind this expectation.

SEE ALSO

Catalyst, Catalyst::View, Catalyst::View::JSON, CatalystX::InjectComponent, Catalyst::Component::InstancePerContext, JSON::MaybeXS

AUTHOR

John Napiorkowski email:jjnapiork@cpan.org

COPYRIGHT & LICENSE

Copyright 2015, John Napiorkowski email:jjnapiork@cpan.org

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.