CGI-Tiny

 view release on metacpan or  search on metacpan

lib/CGI/Tiny/Cookbook.pod  view on Meta::CPAN

=pod

=encoding UTF-8

=head1 NAME

CGI::Tiny::Cookbook - Recipes for advanced CGI::Tiny usage

=head1 DESCRIPTION

L<CGI::Tiny> is a minimal interface to the CGI protocol, but common tasks can
be simplified with the use of other CPAN modules and techniques.

=head1 RECIPES

=head2 Dependencies

CGI scripts which have dependencies, including CGI::Tiny itself, must be run
using the F<perl> which those dependencies have been installed to, and with
access to any nonstandard library installation locations (such as L<local::lib>
or L<Carton>).

Since CGI scripts run in the CGI server's environment, which is usually
different from your user's environment, this means that:

=over

=item *

The CGI script shebang should be an absolute path to the appropriate F<perl>
executable.

  #!/usr/bin/perl

  #!/opt/perl/bin/perl

  #!/home/youruser/perl5/perlbrew/perls/perl-5.34.0/bin/perl

=item *

Nonstandard library locations where dependencies are installed must either be
added to the C<PERL5LIB> environment variable in the CGI server's environment,
or added within the CGI script such as with L<lib> or L<lib::relative>.

  # Apache
  SetEnv PERL5LIB /home/youruser/perl5/lib/perl5

  # Within CGI script
  use lib '/home/youruser/perl5/lib/perl5';

  # Relative to CGI script
  use lib::relative 'local/lib/perl5';

=back

=head2 Fatpacking

L<App::FatPacker> can be used to pack CGI::Tiny, as well as any other pure-perl
dependencies, into a CGI script so that it can be deployed to other systems
without having to install the dependencies there. As a bonus, this means the
script doesn't have to load those modules separately from disk on every
execution.

Just keep in mind that the script will have to be repacked to update those
dependencies, and CGI scripts greatly benefit from efficient XS tools which
cannot be packed this way.

lib/CGI/Tiny/Cookbook.pod  view on Meta::CPAN


    # or from __DATA__
    my $template = data_section __PACKAGE__, 'index.html.ep';
    my $output = $mt->render($template, {foo => $foo});

    $cgi->render(html => $output);
  };

  __DATA__
  @@ index.html.ep
  <html><body><h1><%= $foo %></h1></body></html>

=head2 Files

Modules like L<Path::Tiny> and L<MIME::Types> can help with file responses. Be
aware that Perl and some operating systems work with filenames in encoded
bytes (usually UTF-8), but this module works with parameters in Unicode
characters, so non-ASCII filenames make things trickier.

  #!/usr/bin/perl
  use strict;
  use warnings;
  use utf8;
  use CGI::Tiny;
  use Path::Tiny;
  use MIME::Types;
  use Unicode::UTF8 qw(encode_utf8 decode_utf8);

  cgi {
    my $cgi = $_;

    my $filename = $cgi->query_param('filename');
    unless (length $filename) {
      $cgi->set_response_status(404)->render(text => 'Not Found');
      exit;
    }

    # get files from public/ next to cgi-bin/
    my $public_dir = path(__FILE__)->realpath->parent->sibling('public');
    my $encoded_filename = encode_utf8 $filename;
    my $filepath = $public_dir->child($encoded_filename);

    # ensure file exists, is readable, and is not a directory
    unless (-r $filepath and !-d _) {
      $cgi->set_response_status(404)->render(text => 'Not Found');
      exit;
    }

    # ensure file path doesn't escape the public/ directory
    unless ($public_dir->subsumes($filepath->realpath)) {
      $cgi->set_response_status(404)->render(text => 'Not Found');
      exit;
    }

    my $basename = decode_utf8 $filepath->basename;
    my $mime = MIME::Types->new->mimeTypeOf($basename);
    $cgi->set_response_type($mime->type) if defined $mime;
    $cgi->set_response_disposition(attachment => $basename)->render(file => $filepath);
  };

=head2 Cookies

Cookie values should only consist of ASCII characters and may not contain any
control characters, space characters, or the characters C<",;\>. More complex
strings can be encoded to UTF-8 and L<base64|MIME::Base64> for transport.

  #!/usr/bin/perl
  use strict;
  use warnings;
  use utf8;
  use CGI::Tiny;
  use Unicode::UTF8 qw(decode_utf8 encode_utf8);
  use MIME::Base64 qw(decode_base64 encode_base64);

  cgi {
    my $cgi = $_;

    my $value = $cgi->param('cookie_value');
    unless (defined $value) {
      my $cookie = $cgi->cookie('unicode');
      $value = decode_utf8 decode_base64 $cookie if defined $cookie;
    }

    if (defined $value) {
      my $encoded_value = encode_base64 encode_utf8($value), '';
      $cgi->add_response_cookie(unicode => $encoded_value, Path => '/');
      $cgi->render(text => "Set cookie value: $value");
    } else {
      $cgi->render(text => "No cookie value set");
    }
  };

Data structures can be encoded to JSON and base64 for transport.

  #!/usr/bin/perl
  use strict;
  use warnings;
  use utf8;
  use CGI::Tiny;
  use Cpanel::JSON::XS qw(decode_json encode_json);
  use MIME::Base64 qw(decode_base64 encode_base64);

  cgi {
    my $cgi = $_;

    my $key = $cgi->param('cookie_key');
    my $hashref;
    if (defined $key) {
      $hashref->{$key} = $cgi->param('cookie_value');
    } else {
      my $cookie = $cgi->cookie('hash');
      $hashref = decode_json decode_base64 $cookie if defined $cookie;
      $key = (keys %$hashref)[0] if defined $hashref;
    }

    if (defined $hashref) {
      my $encoded_value = encode_base64 encode_json($hashref), '';
      $cgi->add_response_cookie(hash => $encoded_value, Path => '/');
      $cgi->render(text => "Set cookie hash key $key: $hashref->{$key}");
    } else {
      $cgi->render(text => "No cookie value set");
    }
  };



( run in 0.681 second using v1.01-cache-2.11-cpan-39bf76dae61 )