Blazor, ett första intryck

Den senaste tiden har begrepp eller namn som 'WebAssembly', 'Blazor' och 'C# in the browser' dykt upp överallt. Det finns en uppsjö poddar, filmklipp och skrifter på nätet om detta och alla är mer eller mindre lyriska.
En kollega till mig frågade mig i somras om jag kollat något på det, dvs WebAssembly. Jag sa nej och tänkte att det där är nya Silverlight... Förlåt Emil, nu har jag testat lite och det är otroligt coolt!

Det som fick mig att kolla närmare på det var en pod, eller egentligen ljudet från en presentation, som jag lyssnade på häromdagen. Presentationen av Daniel Roth handlade om nyheter i ASP.Net Core 2.2-preview 2 och finns på Youtube, här https://www.youtube.com/watch?v=DDBmvOPfqzA. Den handlar om en massa andra saker också men Blazor och WebAssembly var det som jag gick igång på.

Resurser

Här kommer användbara länkar i ämnet:

Introdukion

Jag tänkte inte försöka förklara så mycket om tekniken bakom Blazor eller WebAssembly annat än att det är INTE NYA SILVERLIGHT. WebAssembly, WASM, är en ny standard för binär kod som kan exekvera i moderna webbläsare. Man behöver alltså inte installera någon plugin eller extension för att nyttja sajter eller webbapplikationer som använder sig av den här tekniken. Mer info finns här https://webassembly.org/. WebAssembly är i version 1.0, dvs "man kan lita på den versionen".

Vad är då Blazor? Enklaste förklaringen, för dom som är bekanta med ASP.NET MVC och dess view engine Razor, är att förklara vad namnet kommer av. Det är en blandning av Browser + Razor som gav Blazor. Razor är ASP.NET MVCs view engine med stöd för att skriva C#-kod tillsammans med vanlig vy-kod, dvs HTML+CSS+Javascript. Blazor har tagit detta ett eller flera steg längre, mha WebAssembly, så att man kan skeppa binärer till klienten och exekvera dessa. Såklart finns det flera begränsningar för vad man kan göra i koden bakom binärerna eftersom applikationen lever i sandlådan som det aktuella webbläsaren tillåter.

Det finns massor att skriva om det här om man skulle vilja, men det enklaste är att hänvisa till Daniel Roths presentation ovan och sedan till https://blazor.net/. Lyssna, titta och läs! Ni kommer att bli alldeles hänförda.

Blazor är inte i produktionsskick, version 0.6 i skrivande stund, vilket gör att verktygen och stabiliteten inte alltid är 100%. Detta blir såklart bättre och bättre undan för undan, ju närmare en version 1.0 man kommer.

Laboration

Det som jag ville utforska mer var att bygga ett klient-UI i Blazor och en backend i ett vanligt ASP.NET Web Api. Det visade sig vara hur enkelt som helst! Visual Studio-lösningen finns här https://github.com/HeadlightAB/BlazorFirstGlance.

Lösningen är såklart starkt influearad av "Get Started"-sidan här https://blazor.net/docs/get-started.html och den färdiga mallen Blazor (ASP.Net Core hosted), men det känns alltid bra göra själv och se hur sakerna hänger ihop.

Planen var att testa konceptet enligt skissen nedan, ett extremt simpelt scenario med en klient och ett api som ska konsumeras, genom ett GET-anrop.
Exempelsystem

Klient-applikationen kan hostas mer eller mindre varsomhelst, iallafall på dom plattformarna som kan servera statiskt innehåll. Lösningen som presenteras här innebär att Blazor-applikationen serveras av samma host som serverar webapi:et, genom att använda ett middleware som finns för Blazor i ASP.NET Core.

För att skapa Blazor-applikationer måste man, i skrivande stund, installera en extension i Visual Studio som ger oss några nya projektmallar. Hämta VS extension 'ASP.NET Core Blazor Language Services' här https://go.microsoft.com/fwlink/?linkid=870389.

Implementation

Vi börjar från början, i Visual Studio och File -> New Project.

I 9 fall av 10 börjar jag alltid med att skapa en tom lösning, mha mallen under:
Other Project Types -> Visual Studio Solutions -> Blank Solution Blank solution

Anledningen till att jag väljer en tom lösning är att man väldigt ofta inte vill ha samma namn på lösningen som det första projektet. Det är alltså bara tycke och smak, har inget med tekniken att göra.

Api

För att göra det enkelt för mig så skapar jag direkt ett nytt ASP.NET Core Web Api-projekt och väljer att behålla, den av mallen skapade, ValuesController. Strukturen ser nu ut enligt:
Nytt api

Klient

Nästa steg är att lägga till en Blazor-applikation, med hjälp av en av dom mallarna som installerades genom VS extension enligt ovan. Välj att lägga till ett nytt ASP.NET Core Web Application-projekt och välj mallen Blazor:
Blazor client

I det skapade projektet fäller jag ut katalogerna Pages och Shared och behåller endast filer enligt nedan:
Client project

Nu är dom stora delarna på plats och det är dags att försöka få allt att fungera, hostingen av api:et och klientapplikationen i webbläsaren.

Hosting

Det är väldigt rakt på att få klientapplikationen serverad från api:et, en ganska logisk lösning kan man säga.

  • Skapa en projektreferens i api-projektet som pekar på klientapplikationen
  • Installera ett nuget-paket med ett middleware att använda i api:et som gör att klientapplikationen serveras när man surfar till roten på api:et

Första steget görs på vanligt vis, genom högerklick på Dependencies -> Add Reference... i api-projektet. Välj klientprojektet och klicka OK.

Installera nuget-paketet i api-projektet genom följande kommando i Package Manager Console:

install-package Microsoft.AspNetCore.Blazor.Server  

I Startup.cs i api-projektet lägger vi till middlewaret i metoden Configure. Vi pekar ut vilken klass som är klientens startpunkt, TheBlazorClient.Startup:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseMvc();

    app.UseBlazor<TheBlazorClient.Startup>();
}

Nu är det dags att köra igång grejorna, genom att helt enkelt trycka F5 eller CTRL-F5. En webbläsare ska öppnas och något slags UI ska visas om man surfar till roten:
Client running

Inte jätteroligt kanske, men Blazor-applikationen är iallafall igång och kör i webbläsaren.

Jag plockade bort meny-div:en till vänster i Index.cshtml, om det skulle vara så att er applikation inte ser exakt lika ut.

Vill man att klientapplikationen ska serveras via annan sökväg än roten måste man använda sig av app.Map(...)-metoden istället för app.UseBlazor<...>().

...
app.Map("/client", c => c.UseBlazor<TheBlazorClient.Startup>());  
...

Man måste även se till att base href i index.html i Blazor-klienten pekar på samma sökväg:

...
<base href="/client/" />  
...

Surfar man nu till roten så får man en 404 Not Found, men till /client så serveras Blazor-applikationen.

Notera skillnaden i sökvägen, utan '/' på slutet i app.Map(...) och med '/' på slutet i base href i index.htmtl.

Konsumera api:et från klienten

Nu är det dags att få klienten att konsumera api:et. Vi vill i det här exemplet bara visa listan med values som man får om man gör en HTTP GET /api/values i api:et:

HTTP GET /api/values -> ["value1","value2"]  

Värdena presenterar vi i en enkel ul-li-lista på klienten. Index.cshtml ser då ut enligt:

@page "/"
@inject HttpClient ApiClient

<h1>Hello, world!</h1>

Welcome to your new app.

<h1>Here goes the values</h1>  
@if (values == null)
{
    <p>Nothing yet...</p>
}
else  
{
    <ul>
        @foreach (var value in values)
        {
            <li>@value</li>
        }
    </ul>
}

@functions
{
    string[] values;

    protected override async Task OnInitAsync()
    {
        values = await ApiClient.GetJsonAsync<string[]>("/api/values");
    }
}

Några saker att titta närmare på är:

  • @functions är blocket där man skriver sin C#-kod
  • @inject HttpClient ApiClient tyder på att dependency injection finns i Blazor, här injiceras en instans av en HttpClient till variabeln ApiClient
  • Man måste ha null-kontrollen på values, annars dör applikationen

Nu borde listan med values, hämtade från api:et, synas på sidan:
Client with values

Sammanfattning

Ovanstående visade ett väldigt banalt exempel. Det finns en hel del saker med databindningar och annat att utforska i Blazor, men det här kan vara en bra start iallafall. Kanske kan det sätta igång en del funderingar kring att byta ut någon tjock klientapplikation mot en halvtjock Blazor-applikation? Det kommer bli extremt spännande att se vart det här tar vägen, det är ju trots allt en .NET Core-runtime som snurrar i webbläsaren, så det mesta som man kan göra i den körtidsmiljön kan man även göra i sin Blazor/WebAssembly-applikation.

All kod för det här exemplet finns här https://github.com/HeadlightAB/BlazorFirstGlance.