Blazor est un nouveau framework qui arrive avec ASP.NET Core 3, et qui permets de faire du développement web client en C# au lieu de Javascript. Il propose 2 modèles d'hébergement : un mode client, où les dlls sont envoyées au client avec un runtime .NET, et exécutées grâce à WebAssembly ; et un mode serveur, où le code est exécuté par le serveur, et le client reçoit les mises à jour par une connexion SignalR. Pour plus d'informations, je vous invite à aller lire la documentation de Blazor.

Lorsque vous démarrez un nouveau projet ASP.NET Core3, vous avez la possibilité de créer un projet pur Blazor, ou un projet MVC, mais il est tout à fait possible d'ajouter des composants Blazor à une ou plusieurs page d'un projet MVC.

Note : cet article utilise Blazor en mode server-side, et est basé sur la preview 7 - il peut y avoir des changements dans les prochaines versions.

Migration vers ASP.NET Core 3

Blazor est une nouveauté d'ASP.NET Core 3. Si vous démarrez un nouveau projet ASP.NET Core 3, vous pouvez passer au paragraphe suivant.

Si vous souhaitez utiliser Blazor sur un projet existant sur un framework précédent, il vous faudra d'abord le migrer vers la dernière version : comme d'habitude, lisez la documentation pour effectuer la migration, notamment le passage sur la migration du code de routage pour utiliser .UseEndpoints().

Configuration de Blazor dans le Startup.cs

Pour activer Blazor dans le projet, il y a 2 modifications à faire dans le Startup.cs :

  • Dans la méthode ConfigureServices, ajoutez services.AddServerSideBlazor(); : enregistrement du service Blazor
  • Dans la méthode Configure, allez dans le UseEndoints et ajoutez endpoints.MapBlazorHub(); : configuration du hub SignalR utilisé par Blazor

On se retrouve avec le code qui ressemble à ça :

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddServerSideBlazor();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
        endpoints.MapBlazorHub();
    });
}

Ajout d'un composant Blazor dans une page

Pour mon exemple, j'ai repris le composant Counter du template Blazor, sans l'attribut @page

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    int currentCount = 0;

    void IncrementCount()
    {
        currentCount++;
    }
}

Puis on peux référencer le composant depuis notre vue MVC :

@(await Html.RenderComponentAsync<BlazorInMvc.Components.Counter>(RenderMode.Server))

Vous pouvez lancer la page : vous verrez le composant s'afficher avec le "Current count" à 0, et le bouton. Mais si vous cliquez dessus, rien ne se passe. Il faut ajouter un fichier javascript pour établir la connexion SignalR :

@section Scripts{
    <script src="~/_framework/blazor.server.js"></script>
}

Si l'url de votre page n'est pas à la racine du site, la connexion ne pourra pas s'effectuer correctement. Dans ce cas, il faut rajouter un élément dans l'en-tête de votre page pour préciser le dossier où elle se trouve. Si l'url de ma page est /Home/Counter, le dossier est /Home. J'ajoute donc à ma page :

@section Header{
    <base href="~/Home" />
}

Maintenant vous pouvez retester, et le composant fonctionne : les clics sur le bouton mettent bien à jour le compteur.

Utilisation de la navigation Blazor

À priori, l'utilisation de la navigation Blazor avec le routing MVC risque de poser soucis, mais en étant rigoureux dans la définition de ses routes, on peut faire fonctionner les 2 ensembles.

J'ai 2 pages MVC : /Home et /Home/Counter (même si l'une contient un composant Blazor, c'est toujours une page MVC), et je veux créer 2 pages Blazor : /Nav/Page1 et /Nav/Page2, qui fonctionneront en mode SPA lorsqu'on navigue de l'une à l'autre (et seulement entre ces 2).

Pour faire ça, on va créer un contrôleur qui recevra les requêtes vers les pages Blazor, et on configure le routing pour que toutes les pages sous l'url /Nav pointent vers la même Action.

NavController.cs :

public class NavController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Startup.cs :

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "blazor",
        pattern: "Nav/{page?}/{id?}",
        defaults: new { controller = "Nav", action = "Index" }
    );

    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapBlazorHub();
});

L'action Index va contenir un composant Blazor, qui servira de routeur pour les 2 pages :

Nav.razor :

<Router AppAssembly="typeof(Startup).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="routeData" />
    </Found>
    <NotFound>
        <h1>Page not found</h1>
    </NotFound>
</Router>

Puis les composants Blazor correspondant à nos pages :

@page "/Nav/Page1"
@using Microsoft.AspNetCore.Components.Routing

<h3>Page1</h3>

<NavLink href="/Nav/Page2">Aller à la page 2</NavLink>

et

@page "/Nav/Page2"
@using Microsoft.AspNetCore.Components.Routing

<h3>Page2</h3>

<NavLink href="/Nav/Page1">Aller à la page 1</NavLink>

Les composants commencent par l'attribut @page, qui permets de définir à quelle url le composant doit répondre.

Maintenant vous pouvez passer de la page1 à la page2 sans avoir un rechargement complet de la page, la navigation se fait par la connexion SignalR. Si vous naviguer vers ou depuis une autre page, la navigation est une navigation html classique.

Code source

Vous pouvez récupérer le code source de cet article sur ma page Github : https://github.com/glacasa/Blog-BlazorInMvc.

Pour le compiler, vous devez avoir la dernière version de Visual Studio 2019 preview, ainsi que le framework dotnet core 3 preview 9 minimum.