Découverte de NancyFx : un framework web léger pour .NET

NancyFx est un framework web open-source minimaliste pour la plateforme .NET. Il permet de construire des services HTTP sans dépendre d'infrastructures lourdes. Le projet officiel est accessible à l'adresse nancyfx.org.

Principales caractéristiques

NancyFx se distingue par sa philosophie « zéro configuration » : aucun fichier XML à éditer, aucune modification du web.config n'est requise. Son indépendance vis-à-vis de l'hôte permet de l'exécuter dans divers environnements : IIS, WCF, service Windows, application console autonome, etc.

Le framework est également compatible avec Mono, ce qui autorise un déploiement multiplateforme sur Linux et macOS.

Premiers pas avec NancyFx sous .NET Core

Après création d'une application .NET Core, installez le package Nancy via NuGet ou la CLI :

dotnet add package Nancy

Dans Startup.cs, branchez Nancy au pipeline OWIN :

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
        app.UseDeveloperExceptionPage();

    app.UseOwin(pipe => pipe.UseNancy());
}

Créez ensuite votre premier module en héritant de NancyModule :

public class GreetingModule : NancyModule
{
    public GreetingModule()
    {
        Get("/", args => "Bienvenue sur Nancy !");
    }
}

Le premier argument définit le pattern d'URL, le second une fonction lambda retournant la réponse. Lancez l'application : le message s'affiche.

Architecture des modules

Toute classe dérivant de NancyModule est automatiquement détectée par le framwork. Vous pouvez définir un préfixe de route via le constructeur de base :

public class GreetingModule : NancyModule
{
    public GreetingModule() : base("/accueil")
    {
        Get("/", args => "Page d'accueil");
    }
}

L'URL d'accès devient alors /accueil/.

Système de routage

Nancy propose plusieurs types de segments de route :

Segments littéraux

Un chemin fixe comme /accueil correspond à un segment littéral.

Segments avec variable capturée

public GreetingModule()
{
    Get("/etudiants/{id?}", args =>
        args.id == null ? "Aucun identifiant fourni" : $"Étudiant #{args.id}");
}

Le suffixe ? rend le paramètre optionnel.

Segments avec expression régulière

public GreetingModule()
{
    Get(@"/produits/(?`^[0-9]{4}$)", args => $"Code produit : {args.code}");}`

Segments gloutons (Greedy)

L'astérisque en fin de variable capture toute la suite de l'URL :

public class CatalogModule : NancyModule
{
    public CatalogModule()
    {
        Get("/catalogue/{chemin*}", args => args.chemin);
    }
}

Contraintes de type

public class CatalogModule : NancyModule
{
    public CatalogModule()
    {
        Get("/produits/{reference:int}", args => $"Référence : {args.reference}");
    }
}

Seules les valeurs entières déclencheront cette route.

Pour renvoyer une vue statique :

public GreetingModule()
{
    Get("/", args => View["etudiants/liste.html"]);
}

Nancy possède ses propres conventions de localisasion des vues. Pour les personnaliser, implémentez IRootPathProvider :

public class PersonnaliseCheminRacine : IRootPathProvider
{
    public string GetRootPath()
    {
        return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vues");
    }
}

Activez ensuite ce provider via un bootstrapper personnalisé :

public class DemarrageCustom : DefaultNancyBootstrapper
{
    protected override IRootPathProvider RootPathProvider =>
        new PersonnaliseCheminRacine();

    public override void Configure(INancyEnvironment env)
    {
        env.Tracing(enabled: true, displayErrorTraces: true);
        base.Configure(env);
    }
}

Moteur de vues SSVE (Super Simple View Engine)

SSVE est le moteur de templates intégré à Nancy. Il traite les fichiers .sshtml, .html et .htm via des subtsitutions par expressions régulières.

Substitution de variables

Bonjour @Model.Nom, vous avez @Model.Utilisateur.Age ans

Boucles d'itération

@Each.liste
    <li>@Current.Titre</li>
@EndEach

Côté module :

public class EtudiantModule : NancyModule
{
    private static readonly List<etudiant> _etudiants = new()
    {
        new Etudiant { Id = 1, Nom = "Dupont", Visible = true },
        new Etudiant { Id = 2, Nom = "Martin", Visible = false },
        new Etudiant { Id = 3, Nom = "Bernard", Visible = true }
    };

    public EtudiantModule()
    {
        Get("/liste", args => View["liste.html", new { _etudiants }]);
    }
}

public class Etudiant
{
    public int Id { get; set; }
    public string Nom { get; set; }
    public bool Visible { get; set; }
}</etudiant>

Template liste.html :

<table>
    <tbody>
        @Each._etudiants
        @If.Visible
        <tr>
            <td>@Current.Id</td>
            <td>@Current.Nom</td>
        </tr>
        @EndIf
        @EndEach
    </tbody>
</table>

Conditions

Les balises @If et @IfNot acceptent un paramètre booléen. Elles ne peuvent pas être imbriquées :

@IfNot.PossedeUtilisateurs
    Aucun utilisateur enregistré.
@EndIf

Vues partielles

Syntaxe : @Partial['nom_vue'[, Model.Propriete]]

<!-- Sans modèle -->
@Partial['pied_de_page.sshtml']

<!-- Avec propriété du modèle -->
@Partial['detail_etudiant.sshtml', Model.Nom]

Fichier pied_de_page.html :

<footer>Vue partielle : @Model</footer>

Utilisation dans une vue principale :

<table>
    @Each._etudiants
    <tr>
        <td>@Current.Id</td>
        <td>@Partial['pied_de_page.html', Model.Nom]</td>
    </tr>
    @EndEach
</table>

Cycle de vie d'une requête

À l'arrivée d'une requête HTTP, Nancy la fait transiter par son pipeline interne : résolution de la route correspondante, invocation via IRouteInvoker, génération de la réponse, passage par les hooks du bootstrapper pour la gestion des erreurs, puis renvoi au client.

Le projet Nancy n'est plus maintenu depuis 2016. Son approche déclarative du routage reste néanmoins élégante, bien que le moteur de vues SSVE soit plus limité que des solutions comme Razor.

Étiquettes: NancyFx .NET Core OWIN routage HTTP moteur de vues

Publié le 6 juillet à 16h31