https://www.endpointdev.com/blog/tags/cgi/2022-04-19T00:00:00+00:00End Point DevPerl Web Frameworkshttps://www.endpointdev.com/blog/2022/04/perl-web-frameworks/2022-04-19T00:00:00+00:00Marco Pessotto
<p><img src="/blog/2022/04/perl-web-frameworks/nature-wing-black-and-white-web-line-insect-1244977.webp" alt="Spider webs and spiders"></p>
<!-- Photo: https://pxhere.com/en/photo/1244977 CC0 Public Domain -->
<h3 id="cgi">CGI</h3>
<p>When I started programming, back in the day, CGI (the Common Gateway Interface) was still widely
used. Usually the Apache webserver would just execute a script or a
binary with some environment variables set and serve whatever the
executable sent to the standard output, while keeping the standard
error in the logs.</p>
<p>This simple and straightforward mechanism can still
be used for small programs, but larger applications usually want to
save the start-up time and live longer than just a single request.</p>
<p>At that time Perl was used far more often than now, and it had (and still
has) the <a href="https://metacpan.org/pod/CGI">CGI.pm</a> module to help the
programmer to get the job done.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-perl" data-lang="perl"><span style="color:#888">#!/usr/bin/env perl</span>
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">utf8</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">strict</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">warnings</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">CGI</span>;
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$q</span> = <span style="color:#b06;font-weight:bold">CGI</span>-><span style="color:#080;font-weight:bold">new</span>;
<span style="color:#080;font-weight:bold">print</span> <span style="color:#369">$q</span>->header;
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$name</span> = <span style="color:#d20;background-color:#fff0f0">'Marco'</span>;
<span style="color:#080;font-weight:bold">print</span> <span style="color:#369">$q</span>->p(<span style="color:#d20;background-color:#fff0f0">"Hello $name"</span>);
<span style="color:#080;font-weight:bold">print</span> <span style="color:#d20;background-color:#fff0f0">"\n"</span>;
</code></pre></div><p>And it will output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">./cgi.pl
Content-Type: text/html; charset=ISO-8859-1
<p>Hello Marco</p>
</code></pre></div><p>Here the script mixes logic and formatting and the encoding it
produces by default tells us that this comes from another age. But
if you want something which is seldom used and gets executed on demand
without persisting in the machine’s memory, this is still an option.</p>
<p>Please note that there are frameworks which can work in CGI mode, so
there is no reason to use CGI.pm, beside having to maintain legacy
programs.</p>
<h3 id="mojolicious">Mojolicious</h3>
<p>Fast-forward to 2022.</p>
<p>Nowadays Perl is just another language among dozens of them. But it
still gets the job done and lets you write nice, maintainable code like
any other modern language.</p>
<p><a href="https://mojolicious.org/">Mojolicious</a> is currently the top choice if
you want to do web development in Perl. It is an amazing framework,
with a large and active community, and appears to have collected the
best concepts that other web frameworks from other languages have to
offer.</p>
<p>Let’s hack an app in a couple of minutes in a single file, like during
the CGI days:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-perl" data-lang="perl"><span style="color:#888">#!/usr/bin/env perl</span>
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">utf8</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">strict</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">warnings</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">Mojolicious::Lite</span>;
get <span style="color:#d20;background-color:#fff0f0">'/'</span> => <span style="color:#080;font-weight:bold">sub</span> {
<span style="color:#080;font-weight:bold">my</span> (<span style="color:#369">$c</span>) = <span style="color:#369">@_</span>;
<span style="color:#369">$c</span>->stash(name => <span style="color:#d20;background-color:#fff0f0">"Marco"</span>);
<span style="color:#369">$c</span>->render(template => <span style="color:#d20;background-color:#fff0f0">'index'</span>);
};
<span style="color:#b06;font-weight:bold">app</span>->start;
__DATA__
<span style="color:#369">@@</span> <span style="color:#369">index</span>.html.ep
Hello <<span style="color:#369">%</span><span style="color:#a61717;background-color:#e3d2d2">=</span> <span style="color:#a61717;background-color:#e3d2d2">$</span><span style="color:#369">name</span> <span style="color:#369">%</span><span style="color:#a61717;background-color:#e3d2d2">></span>
</code></pre></div><p>Here the structure is a bit different.</p>
<p>First, there’s a Domain Specific Language (DSL) to give you some sugar.
This is the “Lite” version, while in a well-structured Mojolicious app one
prefers to write class methods. We declare that the root (<code>/</code>) URL path of the
application is going to execute some code. It populates the “stash”
with some variables, and finally renders a template which can access
the stashed variables. If you execute the script, you get:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">./mojo.pl cgi 2> /dev/null
Status: 200 OK
Content-Length: 12
Date: Fri, 08 Apr 2022 12:33:52 GMT
Content-Type: text/html;charset=UTF-8
Hello Marco
</code></pre></div><p>The logging to the standard error stream is:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">[2022-04-08 14:33:52.92508] [163133] [debug] [82ae3iV2] GET "/"
[2022-04-08 14:33:52.92532] [163133] [debug] [82ae3iV2] Routing to a callback
[2022-04-08 14:33:52.92565] [163133] [debug] [82ae3iV2] Rendering template "index.html.ep" from DATA section
[2022-04-08 14:33:52.92610] [163133] [debug] [82ae3iV2] 200 OK (0.001021s, 979.432/s)
</code></pre></div><p>This is basically what a modern framework is supposed to do.</p>
<p>The nice thing in this example is that we created a single-file
prototype and launched it as a CGI. But we can also launch it as
daemon and visit the given address with a browser, which is how you
should normally deploy it, usually behind a reverse proxy like
<a href="https://nginx.org/en/">nginx</a>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">./mojo.pl daemon
[2022-04-08 14:48:42.01827] [163409] [info] Listening at "http://*:3000"
Web application available at http://127.0.0.1:3000
[2022-04-08 14:48:48.53687] [163409] [debug] [CwM6zoUQ] GET "/"
[2022-04-08 14:48:48.53715] [163409] [debug] [CwM6zoUQ] Routing to a callback
[2022-04-08 14:48:48.53752] [163409] [debug] [CwM6zoUQ] Rendering template "index.html.ep" from DATA section
[2022-04-08 14:48:48.53808] [163409] [debug] [CwM6zoUQ] 200 OK (0.001209s, 827.130/s)
</code></pre></div><p>If you want you can even launch it with HTTPS as well (please note the
syntax to pass the certificates).</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">./mojo.pl daemon -l <span style="color:#d20;background-color:#fff0f0">'https://[::]:8080?cert=./ssl/fullchain.pem&key=./ssl/privkey.pem'</span> -m production
</code></pre></div><p>For a small application listening on a high port this is already
enough and the whole deployment problem goes away.</p>
<p>Speaking about deployment, Mojolicious has basically no dependencies
other than the core modules and comes with a lot of goodies, for example
a <a href="https://docs.mojolicious.org/Mojo/UserAgent">non blocking user-agent</a>.</p>
<p>Recently a legacy application needed to make some API calls. To speed up
the process, we wanted to make the requests in parallel. And here’s
the gist of the code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-perl" data-lang="perl"><span style="color:#080;font-weight:bold">package</span> <span style="color:#b06;font-weight:bold">MyApp::Async</span>;
<span style="color:#888"># ... more modules here</span>
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">Mojo::UserAgent</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">Mojo::Promise</span>;
<span style="color:#888"># .... other methods here</span>
<span style="color:#080;font-weight:bold">sub</span> <span style="color:#06b;font-weight:bold">example</span> {
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$email</span> = <span style="color:#d20;background-color:#fff0f0">'test@example.com'</span>;
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$ua</span> = <span style="color:#b06;font-weight:bold">Mojo::UserAgent</span>-><span style="color:#080;font-weight:bold">new</span>;
<span style="color:#080;font-weight:bold">foreach</span> <span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$list</span> (<span style="color:#369">$self</span>->get_lists) {
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$promise</span> = <span style="color:#369">$ua</span>->post_p(<span style="color:#369">$self</span>->_url(<span style="color:#d20;background-color:#fff0f0">"/api/v2/endpoint/$list->{code}"</span>),
json => { email => <span style="color:#369">$email</span> })
-><span style="color:#080;font-weight:bold">then</span>(<span style="color:#080;font-weight:bold">sub</span> {
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$tx</span> = <span style="color:#038">shift</span>;
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$res</span> = <span style="color:#369">$tx</span>->result;
<span style="color:#080;font-weight:bold">if</span> (<span style="color:#369">$res</span>->code =~ <span style="color:#080;background-color:#fff0ff">m/^2/</span>) {
<span style="color:#369">$self</span>->_update_db(<span style="color:#369">$data</span>);
}
<span style="color:#080;font-weight:bold">else</span> {
<span style="color:#038">die</span> <span style="color:#369">$tx</span>-><span style="color:#b06;font-weight:bold">req</span>->url . <span style="color:#d20;background-color:#fff0f0">' '</span> . <span style="color:#369">$res</span>->code;
}
});
<span style="color:#038">push</span> <span style="color:#369">@promises</span>, <span style="color:#369">$promise</span>;
}
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$return</span> = <span style="color:#00d;font-weight:bold">0</span>;
<span style="color:#b06;font-weight:bold">Mojo::Promise</span>->all(<span style="color:#369">@promises</span>)-><span style="color:#080;font-weight:bold">then</span>(<span style="color:#080;font-weight:bold">sub</span> { <span style="color:#369">$return</span> = <span style="color:#00d;font-weight:bold">1</span> }, <span style="color:#080;font-weight:bold">sub</span> { <span style="color:#369">$return</span> = <span style="color:#00d;font-weight:bold">0</span>})-><span style="color:#038">wait</span>;
<span style="color:#080;font-weight:bold">return</span> <span style="color:#369">$return</span>;
}
</code></pre></div><p>So a bunch of requests are run in parallel and then synced before
returning. Does it remind you of JavaScript? Of course. A lot of
common paradigms taken from other languages and frameworks were
implemented here, and you can find the best of them in this nice
package.</p>
<p>But the point here is that it doesn’t need dozens of new modules
installed or upgraded. It’s just a single module in pure Perl that you
can even install in your application tree. This is a huge advantage if
you’re dealing with a legacy application which uses an old Perl tree
and you want to play safe.</p>
<p>So, if you’re starting from scratch, go with Mojolicious. It lets you
prototype fast and doesn’t let you down later.</p>
<p>However, starting from scratch is not always an option. Actually, it’s
a rare opportunity. There’s a whole world of legacy applications and
they generate real money every day. It’s simply not possible or even
desirable to throw away something that works for something that would
do the same thing but in a “cooler” way. In ten years, the way we’re
coding will look old anyway.</p>
<h3 id="interchange">Interchange</h3>
<p>Wait. Isn’t <a href="https://www.interchangecommerce.org">Interchange</a> an old
e-commerce framework? Yes, it’s not exactly a generic web framework,
on the contrary, it’s a specialized one, but it’s still a framework and
you can still do things in a maintainable fashion. The key is using
the so-called action maps:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">ActionMap jump <<EOR
sub {
# get the path parameters
my ($action, @args) = split(/\//, shift);
# get the query/body parameters
my $param = $CGI->{param};
# redirect to another page
$Tag->deliver({ location => $final });
# or serve JSON
$Tag->deliver({ type => 'application/json', body => $json_string });
# or serve a file
$Tag->deliver({ type => 'text/plain', body => $bigfile });
# or populate the "stash" and serve a template page
$Tag->tmp(stash_variable => "Marco");
$CGI->{mv_nextpage} = "test.html";
}
EOR
</code></pre></div><p>In <code>pages/test.html</code> you would put this template:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-html" data-lang="html"><<span style="color:#b06;font-weight:bold">p</span>>Hello [scratch stash_variable]</<span style="color:#b06;font-weight:bold">p</span>>
</code></pre></div><p>Now, I can’t show you a simple script which demonstrates this and
you’ll have to take my word for it since we can’t go through the
installation process here for a demo.</p>
<p>Interchange is old, and it shows its
years, but it is actively maintained. It lacks many of Mojo’s
goodies, <em>but</em> you can still do things in a reasonable way.</p>
<p>In the example the code will execute
when a path starting with <code>/jump/</code> is requested. The whole path is
passed to the routine, so you can split at <code>/</code>, apply your logic, and
finally either set <code>$CGI->{mv_nextpage}</code> to a file in the <code>pages</code>
directory or output the response body directly with <code>deliver</code>. This
way you can easily build, as a classical example, an API.</p>
<p>It’s a bit of a poor man’s
<a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC</a>
but it works. That’s basically the core of what a framework like
<a href="https://metacpan.org/pod/Dancer2">Dancer</a> does.</p>
<h3 id="dancer-1--2">Dancer (1 & 2)</h3>
<p>Dancer is basically Ruby’s
<a href="https://github.com/sinatra/sinatra">Sinatra</a> ported to Perl. As
already mentioned, ideas developed in other languages and frameworks
are often ported to Perl, and this is no exception.</p>
<p>Let’s see it in an action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-perl" data-lang="perl"><span style="color:#888">#!/usr/bin/env perl</span>
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">strict</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">warnings</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">Dancer2</span>;
get <span style="color:#d20;background-color:#fff0f0">'/'</span> => <span style="color:#080;font-weight:bold">sub</span> {
<span style="color:#080;font-weight:bold">my</span> <span style="color:#369">$name</span> = <span style="color:#d20;background-color:#fff0f0">"Marco"</span>;
<span style="color:#080;font-weight:bold">return</span> <span style="color:#d20;background-color:#fff0f0">"Hello $name\n"</span>;
};
start;
</code></pre></div><p>Start the script:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">Dancer2 v0.400000 server 22969 listening on http://0.0.0.0:3000
</code></pre></div><p>Try it with <code>curl</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">$ curl -D - http://0.0.0.0:3000
HTTP/1.0 200 OK
Date: Mon, 11 Apr 2022 07:22:18 GMT
Server: Perl Dancer2 0.400000
Server: Perl Dancer2 0.400000
Content-Length: 12
Content-Type: text/html; charset=UTF-8
Hello Marco
</code></pre></div><p>If in the script you say <code>use Dancer;</code> instead of <code>use Dancer2</code>, you get:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-plain" data-lang="plain">$ curl -D - http://0.0.0.0:3000
HTTP/1.0 200 OK
Server: Perl Dancer 1.3513
Content-Length: 12
Content-Type: text/html
X-Powered-By: Perl Dancer 1.3513
Hello Marco
</code></pre></div><p>Dancer’s core doesn’t do much more than routing. And you’ll also
notice that the syntax is very similar to Mojolicious::Lite. So to
get something done you need to start installing plugins which will
provide the needed glue to interact with a database, work with your
template system of choice, and more.</p>
<p>Today you would wonder why you should use Dancer and not Mojolicious,
but when Dancer was at the peak of its popularity the games were still
open. There were plenty of plugins being written and published on CPAN.</p>
<p>Around 2013 Dancer’s development team decided to rewrite it to make
it better. The problem was that plugins and templates needed to be
adapted as well. I’m under the impression that the energy got divided
and the momentum was lost. Now there are two codebases and two
plugin namespaces which do basically the same thing, because for
the end user there is not much difference.</p>
<h3 id="catalyst">Catalyst</h3>
<p>So what was attracting people to Dancer? When Dancer came out, Perl
had a great MVC framework, which is still around,
<a href="http://catalyst.perl.org/">Catalyst</a>. (And note that the main
Mojolicious developer was on the Catalyst team.)</p>
<p>Now, the problem is that to get started with Catalyst, even if it has
plenty of documentation, you need to be already acquainted with a lot
of concepts and technologies. For example, the
<a href="https://metacpan.org/dist/Catalyst-Manual/view/lib/Catalyst/Manual/Tutorial/03_MoreCatalystBasics.pod">tutorial</a>
starts to talk about
<a href="http://www.template-toolkit.org/">Template Toolkit</a> and
the <a href="https://metacpan.org/pod/DBIx::Class">DBIx::Class</a> ORM very early.</p>
<p>These two modules are great and powerful and they deserve to be
studied, but for someone new to modern web development, or even to
Perl, it feels (and actually is) overwhelming.</p>
<p>So, why would you choose Catalyst today? Catalyst has the stability
which Mojo, at least at the beginning, lacked, while
backward compatibility is a priority for Catalyst. The other way to look
at this is that Catalyst doesn’t see much current
<a href="https://metacpan.org/dist/Catalyst-Runtime/changes">development</a>, but
someone could see this as a feature.</p>
<p>Even if Catalyst predates all the hyper-modern features that Mojo has,
it’s still a modern framework, and a good one. I can’t show you a self
contained script (you need a tree of files), but I’d like to show you
what makes it very nice and powerful:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-perl" data-lang="perl"><span style="color:#080;font-weight:bold">package</span> <span style="color:#b06;font-weight:bold">MyApp::Controller::Root</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">Moose</span>;
<span style="color:#080;font-weight:bold">use</span> <span style="color:#b06;font-weight:bold">namespace::autoclean</span>;
<span style="color:#080;font-weight:bold">BEGIN</span> { extends <span style="color:#d20;background-color:#fff0f0">'Catalyst::Controller'</span>; }
<span style="color:#888"># start the chain with /foo/XX</span>
<span style="color:#080;font-weight:bold">sub</span> <span style="color:#06b;font-weight:bold">foo</span> :Chained('/') CaptureArgs(1) {
<span style="color:#080;font-weight:bold">my</span> (<span style="color:#369">$self</span>, <span style="color:#369">$c</span>, <span style="color:#369">$arg</span>) = <span style="color:#369">@_</span>;
<span style="color:#369">$c</span>->stash(name => <span style="color:#d20;background-color:#fff0f0">"$arg"</span>);
}
<span style="color:#888"># /foo/XX/bar/YY</span>
<span style="color:#080;font-weight:bold">sub</span> <span style="color:#06b;font-weight:bold">bar</span> :Chained('foo') Args(1) {
<span style="color:#080;font-weight:bold">my</span> (<span style="color:#369">$self</span>, <span style="color:#369">$c</span>, <span style="color:#369">$arg</span>) = <span style="color:#369">@_</span>;
<span style="color:#369">$c</span>->detach(<span style="color:#369">$c</span>->view(<span style="color:#d20;background-color:#fff0f0">'JSON'</span>));
}
<span style="color:#888"># /foo/XX/another/YY</span>
<span style="color:#080;font-weight:bold">sub</span> <span style="color:#06b;font-weight:bold">another</span> :Chained('foo') Args(1) {
<span style="color:#080;font-weight:bold">my</span> (<span style="color:#369">$self</span>, <span style="color:#369">$c</span>, <span style="color:#369">$arg</span>) = <span style="color:#369">@_</span>;
<span style="color:#369">$c</span>->detach(<span style="color:#369">$c</span>->view(<span style="color:#d20;background-color:#fff0f0">'HTML'</span>));
}
</code></pre></div><p>So, if you hit <code>/foo/marco/bar/test</code> the second path fragment will be
processed by the first method (<code>CaptureArgs(1)</code>) and saved in the
stash. Then the second <code>bar</code> method will be chained to it and the
<code>name</code> will be available in the stash. The last method will be hit
with <code>/foo/marco/another/test2</code>. (Incidentally, please note that
Mojolicious has
<a href="https://docs.mojolicious.org/Mojolicious/Guides/Routing#Nested-routes">nested</a>
routes as well.)</p>
<p>Now, I think it’s clear that in this way you can build deep hierarchies
of paths with reusable components. This works really great with the
DBIx::Class ORM, where you can
chain queries as well. As you can imagine, this is far from a simple
setup. On the contrary, this is an advanced setup for people who
already know their way around web frameworks.</p>
<h3 id="conclusion">Conclusion</h3>
<p>So, to sum up this excursion in the amazing land of Perl web
frameworks: If you build something from scratch, go with Mojolicious.
It’s your best bet. If nothing else, it’s super-easy to install, with
basically no dependencies.</p>
<p>However, there’s no need to make a religion
out of it. Rewriting code without a clear gain is a waste of time and
money. A good developer should still be able to write maintainable
code with the existing tools.</p>