Frameworks courants¶
L'écosystème web de Perl s'est considérablement modernise autour de la spécification PSGI et de sa couche d'abstraction Plack. Mojolicious, Dancer2 et Catalyst représentent les trois grandes approches du développement web Perl, de l'ultra-léger au full-stack. Ce chapitre présente chaque framework avec un exemple minimal de route et un tableau comparatif.
Vue d'ensemble¶
| Framework | Style | Taille | Cas d'usage |
|---|---|---|---|
| Mojolicious | Full-stack, async | Moyen (~80 modules) | Web apps, APIs REST, temps réel |
| Dancer2 | Micro-framework | Léger (~40 modules) | APIs simples, prototypage rapide |
| Catalyst | MVC complet | Lourd (~100+ modules) | Applications d'entreprise |
| CGI::Application | CGI legacy | Très léger | Maintenance legacy |
| Plack/PSGI | Middleware | Infrastructure | Base de tous les frameworks |
Plack et PSGI : le socle commun¶
PSGI (Perl Web Server Gateway Interface) est la spécification qui définit comment les applications Perl communiquent avec les serveurs web. Plack est l'implémentation de référence de cette spécification.
PSGI est l'équivalent de WSGI (Python) ou Rack (Ruby). Tous les frameworks modernes Perl (Mojolicious, Dancer2, Catalyst) peuvent s'exécuter sur n'importe quel serveur PSGI.
# Application PSGI minimale (app.psgi)
use strict;
use warnings;
# Une application PSGI est simplement un coderef
my $app = sub {
my ($env) = @_; # $env contient les variables de la requete
my $method = $env->{REQUEST_METHOD};
my $path = $env->{PATH_INFO};
# Reponse : [status, [headers], [body]]
return [
200,
[ 'Content-Type' => 'text/plain' ],
[ "Methode: $method, Chemin: $path\n" ],
];
};
# Lancement avec : plackup app.psgi
Middlewares Plack utiles¶
# Empilement de middlewares avec Plack::Builder
use Plack::Builder;
use Plack::Middleware::Logger;
use Plack::Middleware::Static;
use Plack::Middleware::Session;
my $app = sub { ... };
builder {
# Servir les fichiers statiques depuis /public
enable 'Static',
path => qr{^/static/},
root => './public';
# Journalisation des requetes
enable 'Logger';
# Gestion des sessions
enable 'Session';
$app;
};
Mojolicious¶
Mojolicious est un framework web full-stack, moderne et sans dépendances externes (sauf le core Perl). Il supporte nativement l'asynchrone via des promises et un event loop intégré (Mojo::IOLoop).
Choix recommande en 2025
Mojolicious est le framework Perl le plus actif et le mieux documente. Il est le choix par défaut pour un nouveau projet Perl web en 2025.
Installation¶
Application complète Mojolicious¶
#!/usr/bin/perl
use v5.36;
use Mojolicious::Lite -signatures;
# Middleware de logging
app->log->level('debug');
# Route GET simple
get '/' => sub ($c) {
$c->render(json => { message => 'Bonjour depuis Mojolicious' });
};
# Route avec parametre de chemin
get '/utilisateurs/:id' => sub ($c) {
my $id = $c->param('id');
$c->render(json => { id => $id, nom => "Utilisateur $id" });
};
# Route POST avec body JSON
post '/utilisateurs' => sub ($c) {
my $data = $c->req->json;
# Validation basique
unless ($data && $data->{nom}) {
return $c->render(
status => 400,
json => { erreur => 'Le champ nom est requis' }
);
}
$c->render(
status => 201,
json => { id => 42, nom => $data->{nom} }
);
};
# Demarrage du serveur
app->start;
# Lancement : perl app.pl daemon -l http://*:3000
Mojolicious full (avec classes)¶
# lib/MonApp.pm
package MonApp;
use Mojo::Base 'Mojolicious', -signatures;
sub startup ($self) {
# Configuration
$self->config(secret => 'ma-cle-secrete');
# Routes
my $r = $self->routes;
$r->get('/')->to('items#index');
$r->post('/items')->to('items#create');
$r->get('/items/:id')->to('items#show');
}
1;
Dancer2¶
Dancer2 est un micro-framework inspire de Sinatra (Ruby) et Flask (Python). Il privilegit la simplicité et la lisibilite pour les petites applications et les APIs.
Application Dancer2¶
#!/usr/bin/perl
use v5.36;
use Dancer2;
# Configuration
set serializer => 'JSON';
set port => 3000;
# Route GET
get '/sante' => sub {
return { statut => 'ok', version => '1.0' };
};
# Route avec parametre
get '/articles/:slug' => sub {
my $slug = route_parameters->get('slug');
return { slug => $slug, titre => "Article: $slug" };
};
# Route POST
post '/articles' => sub {
my $body = request->data;
unless ($body->{titre}) {
status 400;
return { erreur => 'titre requis' };
}
status 201;
return { id => int(rand(1000)), titre => $body->{titre} };
};
# Demarrage
dance;
Dancer2 vs Dancer
Utilisez toujours Dancer2, jamais l'original Dancer (v1). Dancer2 est une réécriture complète avec une meilleure isolation des applications.
Catalyst¶
Catalyst est le framework MVC "enterprise" de Perl. Il est très configurable mais nécessité plus de code boilerplate. Convient aux grandes applications avec des équipes structurées.
Structure d'une application Catalyst¶
# lib/MonApp/Controller/Root.pm
package MonApp::Controller::Root;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller' }
# Action racine
sub index : Path : Args(0) {
my ($self, $c) = @_;
$c->response->body('Bienvenue sur Catalyst');
}
# Action avec parametre
sub utilisateur : Path('/utilisateur') : Args(1) {
my ($self, $c, $id) = @_;
# Acces au modele
my $user = $c->model('DB::Utilisateur')->find($id);
if ($user) {
$c->response->content_type('application/json');
$c->response->body(
$c->model('JSON')->encode({ id => $user->id, nom => $user->nom })
);
} else {
$c->response->status(404);
$c->response->body('Utilisateur non trouve');
}
}
__PACKAGE__->meta->make_immutable;
1;
Écosystème complementaire¶
Accès aux bases de données¶
| Module | Rôle | Usage |
|---|---|---|
| DBI | Interface universelle BDD | Requêtes SQL directes |
| DBIx::Class | ORM complet | Modèles, relations, migrations |
| Mojo::Pg | PostgreSQL async | Avec Mojolicious |
| Mojo::SQLite | SQLite async | Développement, tests |
# DBI : connexion et requetes directes
use DBI;
my $dbh = DBI->connect(
'dbi:SQLite:dbname=ma_base.db',
undef, undef,
{ RaiseError => 1, AutoCommit => 1 }
);
# Requete preparee (protection contre injection SQL)
my $sth = $dbh->prepare('SELECT id, nom FROM utilisateurs WHERE actif = ?');
$sth->execute(1);
while (my $row = $sth->fetchrow_hashref) {
printf "ID: %d, Nom: %s\n", $row->{id}, $row->{nom};
}
$dbh->disconnect;
OOP moderne : Moose et Moo¶
# Moo : OOP legere et rapide (recommande pour nouveaux projets)
package Produit;
use Moo;
use Types::Standard qw(Str Num Int);
# Attributs avec types et valeurs par defaut
has nom => (is => 'rw', isa => Str, required => 1);
has prix => (is => 'rw', isa => Num, default => 0);
has stock => (is => 'rw', isa => Int, default => 0);
# Methode
sub en_stock {
my ($self) = @_;
return $self->stock > 0;
}
package main;
use v5.36;
my $p = Produit->new(nom => 'Clavier', prix => 49.99, stock => 10);
say $p->nom; # Clavier
say $p->en_stock; # 1