Aller au contenu

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

cpanm Mojolicious

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

Journalisation

# Log::Any : interface de logging universelle
use Log::Any qw($log);
use Log::Any::Adapter ('Stdout', log_level => 'info');

$log->info('Application demarree');
$log->debugf('Requete recue : %s %s', $method, $path);
$log->errorf('Erreur base de donnees : %s', $err);