facebook

Blog

Resta aggiornato

Vediamo come possiamo realizzare una Single Page Application utilizzando C# invece di JavaScript
Creare una Single Page Application in C# con Blazor
mercoledì 23 Ottobre 2019

Gli sviluppatori Microsoft hanno sempre avuto un brutto rapporto con JavaScript e Microsoft ha sempre tentato di aiutarli con i suoi framework. ASP.NET WebForm, SilverLight, Lightswitch, sono probabilmente i principali esempi in questo senso. Con ASP.NET MVC e .NET Core sembrava fosse finita, fino all’annuncio di un nuovo framework per lo sviluppo full-stack in C#: Blazor.

Passando da un applicazione desktop ad una applicazione web, il requisito più importante è restituire all’utente un’esperienza comparabile, senza refresh e con un minimo di supporto offline, almeno finché non ci sia la necessità di recuperare o salvare dati. Questi requisiti hanno portato alla separazione, nell’application layer, tra API di backend e Single Page Application di front-end capace di girare nel browser, che significa scriverla in JavaScript.

L’obiettivo di Blazor è permettere di far girare codice .NET nel browser senza l’installazione di un plug-in, ma usando il supporto nativo dei principali browser sul mercato per WebAssembly (https://webassembly.org/). Attualmente, questa soluzione è in pre-release e sarà rilasciata a Maggio 2020, possiamo provarla scaricando .NET Core 3.1 da https://dotnet.microsoft.com/download/dotnet-core/3.1 e installando il template di progetto dalla riga di comando:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview1.19508.20

Dopo l’installazione del template, possiamo creare un progetto Blazor Webassembly con il comando:

dotnet new blazorwasm

Adesso, possiamo eseguire l’applicazione con il classico comando:

dotnet run

Aprendo il browser all’indirizzo localhost:5000 possiamo vedere il risultato:

Ma la cosa più interessante è quello che viene scaricato quando apriamo l’app:

Come possiamo vedere, ci sono tre file che rendono possibile la magia: blazor.webassembly.js, mono.js and mono.wasm. I file mono.js e mono.wasm sono la versione WebAssembly del framework Mono, famoso per il porting delle specifiche .NET su sistemi operativi non-Windows prima dell’avvento del framework .NET Core. Il file blazor.webassembly.js usa il runtime Mono per eseguire il codice .NET della nostra applicazione nel browser, codice che si trova nel file Blazorwasm.dll.

Tutto il codice viene eseguito nel browser: non abbiamo bisogno di un web server per eseguire la parte client della nostra applicazione. Possiamo ospitare il risultato della pubblicazione ovunque, anche su un Blob di Azure o una CDN. Se apriamo il codice del progetto, possiamo vedere che lo Startup.cs contiene solo una istruzione nel metodo Configure:

using Microsoft.AspNetCore.Components.Builder;
using Microsoft.Extensions.DependencyInjection;
  
namespace blazorwasm
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services) {}
        public void Configure(IComponentsApplicationBuilder app)
        {
            app.AddComponent<App> ("app");
        }
    }
}

L’interfaccia utente della nostra applicazione sarà composta da un albero di componenti Razor, a partire dalla radice configurata con l’extension method AddComponent. Un componente Razor è una parte della nostra interfaccia grafica, che è definita in un file .razor e che contiene HTML e altri componenti Razor. Se conoscete ASP.NET MVC, la parola Razor è sicuramente familiare: possiamo definire l’interfaccia con una versione customizzata del Razor Engine.

Ad esempio, il component radice App.razor contiene la seguente struttura:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address. </p>
        </LayoutView>
    </NotFound>
</Router>

Questo componente è una composizione di altri componenti (Router, Found, NotFound, LayoutView) e di un paragrafo. Se avete familiarità con i framework di UI JavaScript come Angular, questa struttura è veramente semplice da capire: il componente router ci permette di gestire le rotte nella Single Page Application e reagire se il component richiesto viene trovato oppure no.

Quando definiamo un Razor component, possiamo usare il suo nome come elemento XML, in questo caso <app></app>:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title >blazorwasm </title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/site.css" rel="stylesheet" />
</head>
<body>
    <app >Loading... </app>
    <script src="_framework/blazor.webassembly.js" > </script>
</body>
</html>

Quindi, l’applicazione è composta da classici elementi HTML 5, CSS, il component Razor radice e lo script blazor.webassembly.js. A partire dal component radice App, viene renderizzato l’intero albero di componenti Razor.

Il component MainLayout, nella cartella Shared, mostrerà lo scheletro della pagina, come il _Layout.cshtml o la MasterPage di ASP.NET:

@inherits LayoutComponentBase
  
<div class="sidebar">
    <NavMenu />
</div>
  
<div class="main">
    <div class="top-row px-4">
        <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
    </div>
  
    <div class="content px-4">
        @Body
    </div>
</div>

Nel menù laterale, il motore renderizza il component Razor <NavMenu />, mentre nell’area principale della nostra applicazione renderizza il @Body, che dipende dalla rotta corrente. Come fa il motore a sapere che MainLayout è il componente da usare per il layout della nostra applicazione? Nel componente App è specificato con la seguente riga:

<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" / >

Supponiamo di cliccare sulla voce di menu Counter. Il @Body diventerà il componente Counter.razor, che non è un componente qualsiasi, ma una pagina:

@page "/counter"
  
<h1 >Counter </h1 >
  
<p >Current count: @currentCount </p >
  
<button class="btn btn-primary" @onclick="IncrementCount" >Click me </button >
  
@code {
    private int currentCount = 0;
  
    private void IncrementCount()
    {
        currentCount++;
    }
}

Questa pagina contiene un semplice bottone che esegue codice C# quando viene cliccato. Il codice incrementa una variabile locale, ma la cosa interessante è che viene eseguito nel browser e non è JavaScript:

È un framework molto interessante, ma non è ancora pronto. Molti partner Microsoft, come SyncFusion, Telerik e DevExpress, stanno lavorando su librerie di componenti Razor per supportare gli sviluppatori nella creazione di applicazioni. Questo perché oggi un altro problema per un’applicazione reale è l’assenza di un ecosistema di librerie.

Quindi, cosa possiamo fare oggi? Sicuramente, cominciare a studiare questa tecnologia, ma anche usare una versione server-side di questo framework che Microsoft ha rilasciato con .NET Core 3.0 in RTM. Questa versione sposta il codice e l’elaborazione della UI sul server e usa le WebSocket con SignalR per pushare gli aggiornamenti di interfaccia al browser.

Non abbiamo il supporto offline e non utilizziamo il supporto del browser per WebAssembly, ma in alcuni scenari questi non sono problemi, ma soluzioni. Possiamo far girare l’applicazione anche su browser che non hanno il supporto per WebAssembly, la dimensione dell’applicazione da scaricare è molto piccola, possiamo recuperare direttamente i dati senza problemi di sicurezza perché siamo già sul server.

Andremo più a fondo con questa versione di Blazor nei prossimi articoli, ma possiamo già dire una cosa: quando avremo entrambe le versioni di Blazor, sceglieremo una della due sulla base dei nostri requisiti.

Happy coding!