Explanation of template and JSON rendering render method in Mojolicious

Draw text

To render the text, use the text option of the render method of the Mojolicious::Controller class.

$c->render(text =>'Hi there!');

The internal string is automatically converted to a UTF-8 byte string. Note that the argument is a Perl internal string.

Below is a sample of Mojolicious and Mojolicious::Lite.

#Mojolicious::Lite
get'/' => sub {
  my $self = shift;
  $self->render(text =>'Hi there!');
};;

#Mojolicious
package MyApp::Picture;

use Mojo::Base'Mojolicious::Controller';

sub list {
  my $self = shift;
  $self->render(text =>'Hi there!');
}

Note: The render_text method has been removed in Mojolicious 4.0.

Draw binary data

Use the data option of the Mojolicious::Controller class render method to render the binary data.

$c->render(data =>'some data');

Binary data is data such as images (jpeg, png, gif), audio (mp3), and videos.

Below is a sample of Mojolicious and Mojolicious::Lite.

#Mojolicious::Lite
get'/' => sub {
  my $self = shift;
  $self->render(data =>'some data');
};;

#Mojolicious
package MyApp::Picture;

use Mojo::Base'Mojolicious::Controller';

sub image {
  my $self = shift;
  $self->render(data =>'some data');
}

Note: The render_data method has been removed in Mojolicious 4.0.

Draw JSON

To render the JSON, use the json option of the render method of the Mojolicious::Controller class.

my $data = {name =>'Ken', age => 19};
$c->render(json => $data);

If you pass Perl data, it will be rendered as JSON.

// JSON
{"name": "Ken", "age": 19}

Note that the string contained in Perl's data must be an internal string. Perl's internal strings are automatically converted to UTF-8 byte strings.

Below is a sample of Mojolicious and Mojolicious::Lite.

#Mojolicious::Lite
get'/' => sub {
  my $self = shift;
  my $data = {name =>'Ken', age => 19};
  $self->render(json => $data);
};;

#Mojolicious
package MyApp::Picture;

use Mojo::Base'Mojolicious::Controller';

sub json_data {
  my $self = shift;
  my $data = {name =>'Ken', age => 19};
  $self->render(json => $data);
}

Note: The render_json method has been removed in Mojolicious 4.0.

Draw template

Use the render method of the Mojolicious::Controller class to render the template. Use template as a key.

$c->render(template => $file);

If no arguments are given, the template corresponding to the root name or the template corresponding to the controller name and action name will be drawn.

#Template corresponding to the root name,
# Or draw a template corresponding to the controller name and action name
$c->render;

For example, in the case of Mojolicious::Lite, if you specify the root name, the corresponding template "index.html.ep" will be drawn.

get'/' => sub {
  my $self = shift;
  $self->render(template =>'index');
};;

app->start;

__DATA__

@@index.html.ep
<html>
...
</html>

In the case of Mojolicious, the template "picture / list.html.ep" corresponding to the controller name and action name is drawn.

package MyApp::Controller::Picture;

use Mojo::Base'Mojolicious::Controller';

sub list {
  my $self = shift;
  $self->render;
}

Changes in Mojolicious 6

The default location for controller class namespaces has changed from "MyApp" to "MyApp::Controller". MyApp is still available, but it may be deprecated in the future, so be sure to create your controller class with the "MyApp::Controller ::" prefix.

Draw ZIP data in memory

To draw ZIP data in memory (return as HTTP response):

use Mojolicious::Lite;
get'/' => sub {
  #ZIP creation
  my $zip = Archive::Zip->new;
  $zip->addDirectory('dir1');
  
  #Output the contents of ZIP to a scalar variable
  my $content;
  open my $fh,'>', \ $content;
  binmode $fh;
  $zip->writeToFileHandle($fh);
  close $fh;
  
  #Specify the name of the ZIP file
  $self->res->headers->content_disposition("attachment; filename = foo.zip");
  
  #Draw as ZIP
  return $self->render(data => $content, format =>'zip');
};;
app->start;

Use the Archive::Zip module to create the ZIP data. I am adding a directory using the addDirectory method of the Archive::Zip module.

Archive::Zip does not have a function to output to a scalar variable, only a function to output to a file handle, so I use Perl's scalar input / output function to output to a scalar variable.

You can name the file using the Content-Disposition header.

When drawing, ZIP is binary data, so use the data option. If you specify zip for format, "application / zip" will be set automatically for Content-Type.

Draw CSV data

To draw CSV data (return as HTTP response):

use Mojolicious::Lite;
use Text::CSV::Encoded;
use utf8;
get'/' => sub {
  my $self = shift;
  
  #Create Text::CSV::Encoded object
  my $csv = Text::CSV::Encoded->new({encoding_out =>'UTF-8'});
  
  #Rows
  my $rows = [
    ['Perl','Ken'],
    ['Ruby','Tom']
  ];;
  
  # Scalar I / O
  my $content ='';
  open my $fh,'>>', \ $content;
  
  #Print csv data to scalar variable
  for my $row (@$rows) {
    $csv->print($fh, $row);
    print $fh "\ n";
  }
  
  #HTTP Headers
  my $headers = $self->res->headers;
  $headers->content_disposition('attachment; filename = pictures.csv');
  $headers->content_type('text / csv; charset = UTF-8');
  
  #Render data
  return $self->render(data => $content);
};;
app->start;

Use the Text::CSV::Encoded module to create CSV data. The only way to convert the data of one record to a row is to use the print method, which is a method for printing to a filehandle. Therefore, Perl's scalar input / output function is used to output to scalar variables.

You can name the file using the Content-Disposition header. Let's also specify the Content-Type so that we can see that it is CSV data. When drawing, CSV data is binary data (not an internal string), so use the data option.

Specify the class that describes the template when drawing

You can specify template_class in the argument of the render method for the class that describes the template when drawing.

$c->render(template_class => $class);

This is a template written directly in a plugin etc.It will be very convenient if you want to mention it. Mojolicious plugins are very flexible and allow you to write and incorporate the web application itself.

package Mojolicious::Plugin::Viewer;
use Mojo::Base'Mojolicious::Plugin';

sub register {
  my ($self, $app, $conf) = @_;
  
  my $r = $app->route;
  $r->get('/ viewer', sub {
     my $self = shift;
     #Specify template class
     return $self->render(template_class => __PACKAGE__);
  });
}

1;

__DATA__

@@viewer.html.ep
<html>
  <head>
    <title> Viewer </title>
  <body>
     Viewer <%= lc'A'%>
  </body>
</html>

Dynamically deliver large files

What if you want to dynamically deliver large files in your web application? For example, if you don't know the data size and want to distribute it part by part. If you put everything on the memory, the memory may be overwhelmed, so I don't want to put everything on the memory.

In such a case, use HTTP chunk encoding and deliver part by part. From the browser's point of view, it looks like one file in the end, so there is no problem.

This can be achieved by combining a method called write_chunk with a callback.

get'/ sizeunknowndata' => sub {
  my $self = shift;

  my $file ='foo.tar.gz';
  open my $fh,'<', $file
    or die "Error";

  #Write chunk
  $self->res->headers->content_type('application / gzip');
  $self->res->headers->content_disposition(qq / attachment; filename = "$file" /);
  my $cb;
  $cb = sub {
    my $c = shift;
    my $size = 500 * 1024;
    my $length = sysread($fh, my $buffer, $size);
    unless (defined $length) {
      close $fh;
      undef $cb;
      return;
    }
    $c->write_chunk($buffer, $cb);
  };;
  $self->$cb;
};;

When there is no more data to read, close the filehandle, make $cb undefined, and remove the reference.

Addition

The title "Deliver a large file" was misunderstood. Changed the title to "Deliver dynamically large files".

Large static files can usually be delivered under "public" without straining memory. Also, since Mojolicious is an asynchronous I / O, processing will not be blocked.

Draw a 404 Not Found page

Use reply->not_found to draw a 404 Not found page (the page to display when the page is not found). It was

get'/' => sub {
  my $sefl = shift;
  
  If you want to draw a # 404 not found page
  if (...) {
    return $self->reply->not_found;
  }

  $self->render;
}

Changes in Mojolicious 6

The rendering of the 404 Not Found page has changed from render_not_found to reply->not_found.

Draw an exception (error) page

You may want to show the error page to the user if something goes wrong with unintended processing. Use reply->exception to draw such a page. It was

get'/' => sub {
  my $sefl = shift;
  
  #Draw an exception (error) page
  if (...) {
    return $self->reply->exception;
  }

  $self->render;
}

Changes in Mojolicious 6

The render_exception method for rendering exception pages has been changed to reply->exception.

How to use JSON::XS to draw JSON

Mojolicious has its own module that draws JSON called Mojo::JSON, which is written in pure Perl. If you want to use JSON::XS to render JSON fast, I think it's a good idea to replace the handler for JSON as follows:

use Mojolicious::Lite;
use Mojo::JSON::XS;

Replace handler for drawing # json
app->renderer->add_handler(json => sub {
  my ($self, $c, $output, $options) = @_;
  $$output = Mojo::JSON::XS->new->encode($options->{json});
});

get'/' => sub {
  my $self = shift;
  
  $self->render(json => {name =>'kimoto'});
};;

app->start;

Mojo::JSON::XS is a module that has the same interface as Mojo::JSON and uses JSON::XS internally. It's included in a package called Mojo::JSON::Any, so install it when you use it.

cpan Mojo::JSON::Any

Associated Information