We discuss the three project types available with Blazor 0.5.1, get an understanding of what scenarios to apply them to, and explore what to expect from each.
Blazor is a single-page web application (SPA) framework built on .NET that runs in the browser with Mono's WebAssembly run-time, or server-side via SignalR. Blazor features a component architecture, routing, a virtual DOM, and a JavaScript Interoperability (interop) API. Currently, Blazor is in an experimental state which allows for rapid development, iterations, and as implied, experimentation.
Blazor shipped 0.1 in March of 2018, and within a few months rolled out 0.5.1. In Blazor's short, roughly six month history it has already launched a variety of project types to on-board developers with varying application needs. In this article well discuss the three project types available as of 0.5.1, we'll get an understanding of what scenarios to apply them to, and what to expect from each. We'll jump in and evaluate the server-side, client-side, and full-stack. If you think you've seen it all, make sure to peek at the Server-Side project type as it may turn out to be quite different that one might expect.
Just so we all get started on the same page, let's begin with Blazor's prerequisites.
Blazor is an unsupported experimental web framework that shouldn't be used for production workloads at this time.
As of writing, Blazor is shipping at a rate of once every two months. Because of the project's nature, new tooling must be installed before the Blazor project templates are available in Visual Studio. The same applies to the dotnet
command line interface (CLI).
If you're the type of developer who spends a majority of their time in Visual Studio then you're in luck, Visual Studio is likely to provide the best experience while Blazor is in its infancy, however the dotnet CLI is still quite handy.
Setting up Visual Studio requires the following steps:
Important!
The default global.json file included in the Blazor project templates may cause the project to fail to load or run if you don't have version 2.1.3xx of the .NET Core SDK installed. The global.json file pins the project to 2.1.3xx; so if you don't have that specific version range installed, the project fails to load or run even if you have a newer SDK installed. The workaround is to remove the global.json file from the project or install version 2.1.302 of the .NET Core SDK.
This installation process will add the three project types we'll be covering below. Blazor Component Library project, the fourth and final project type, is only available from the CLI at this time. The Blazor Component Library is outside the scope of this article since it isn't an executable project type.
To make the templates available from the dotnet
CLI, open a command console and run the following command:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates
With the tooling installed we're ready to run and explore the Blazor projects. The New ASP.NET Core Web Application dialog is where the new project types are found.
To reveal the dialog:
Let's breakdown the project types from left to right:
* Denotes unofficial short description
Now that we know were to find each project template, we can discuss the details of each selection.
The Blazor client-side template can be compared to an HTML, CSS, and JavaScript application, similar to other SPA frameworks. However, in the case of Blazor, JavaScript is supplemented by .NET and C# via WebAssembly. Unlike ASP.NET MVC projects that use Razor, Blazor (client-side) is not rendered on the server, instead the HTML is delivered to browser via the Blazor framework on the client. So in essence the browser processes everything in this project is a static resource.
Since the project can be treated as a set of static resources, it leads to some interesting possibilities that have yet to be seen in the ASP.NET ecosystem. Projects built with this template can be hosted on virtually any resource than can serve static files, for example: GitHub pages and Azure Storage: Static website hosting.
The template includes examples of how to get started with Blazor as well as basic page layouts, navigation controls, and styling from Bootstrap. The project structure is fairly simple with just the few resources outlined below:
/wwwroot
: web standard static resources including: CSS, JavaScript, JSON, images, and HTML/Pages
: Razor (.cshtml
) application pages/features/_Shared
: common (.cshtml
) components & page layoutsApp.cshtml
: a temporary file, to be removed in later versions of BlazorProgram.cs
and Startup.cs
: application bootstrapping and configurationAt first glance, some familiar concepts may appear as Blazor uses an approach similar to ASP.NET apps. Not only is Program.cs
and Startup.cs
a common feature in .NET apps, but Blazor also utilizes a similar concept to ASP.NET Core Razor Pages. Blazor application pages or features can be found under the Pages
project path, while routing is handled by the page's @page
directive. In the Blazor framework, a view or .cshtml
is treated as a web component including those marked with the @page
directive.
@page "/myRoute"
<!-- component markup -->
@functions {
// component logic
}
All Blazor projects include an Index, Counter, and Fetch Data example pages. These items are consistent throughout all project types, except for Fetch Data, as the key difference between each project type is where the application is hosted in relation to the data it consumes.
Let's begin by examining the Counter page and its component markup and logic.
The Counter page is a simple component decorated with the page directive. This component demonstrates the basic composition of a Blazor component including: routing, data binding, and event binding/handling.
The counter component uses a basic HTML button to increment a counter field which is displayed within a paragraph tag. Because Blazor operates as a single page application all of the interactions in the component happen on the client. Updates to the browser's Document Object Model (DOM) are handled by the Blazor framework though data binding.
Moving on to Fetch Data page, we'll see how Blazor is capable of handing local data sources.
In this project type, the Fetch Data page is a component that utilizes data from a local static file. The Fetch Data component demonstrates dependency injection and basic Razor template concepts. This version of Fetch Data is very similar to the example found in the Full-Stack template except for the location in which the data is loaded from.
At the top of the component following the routing directive dependency injection is declared. The @inject
directive instructs Blazor to resolve an instance of HttpClient
to the variable Http
. The HttpClient
is then used by the components logic to fetch data using GetJsonAsync
which binds data from the JSON request to an array of WeatherForecast
objects:
@page "/fetchdata"
@inject HttpClient Http
// ... markup omitted for brevity
@functions {
WeatherForecast[] forecasts;
protected override async Task OnInitAsync()
{
forecasts = await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}
// ...
}
Displaying the WeatherForecast
data is done by iterating over the forecasts
collection and binding the values to an HTML table:
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>...</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
No server is used or needed for these basic examples. If we plan to develop our application with hosting and web services then the Full-Stack or Server-Side templates may be a better starting point.
The Blazor Full-Stack template encompasses the same project structure as the Client-Side template with a few additions. Just like the Client-Side template there is no HTML rendered by the server and all files are delivered to the client as static files including .NET binaries. The difference however is added ASP.NET Core hosting and Web API and a Shared project for common application logic.
The template includes three projects: a Client-Side Blazor application Blazor.Client
, an ASP.NET Core server application Blazor.Server
, and a shared .NET Standard project for common application logic Blazor.Shared
.
The server application is responsible for serving the application and providing web API endpionts for data. In Startup.cs
we'll find the MimeType
settings which configure the server to allow *.wasm
and *.dll
files to be served. In addition, compression is enabled to help reduce the size of binary files as the are transferred to the client. Compression is enabled through the AddResponseCompression
middleware.
services.AddResponseCompression(options =>
{
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
{
MediaTypeNames.Application.Octet,
WasmMediaTypeNames.Application.Wasm,
});
});
The Startup process also is where the Blazor application middleware is initialized by app.UseBlazor<Client.Program>
. This identifies the Blazor.Client
application which is being served.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseResponseCompression();
// ...
app.UseBlazor<Client.Program>();
}
The application comes with a simple example of a Web API controller and action. In the SampleDataController
, a WeatherForecasts
action generates a random set of weather forecasts. In the Full-Stack template, the WeatherForecasts
web API replaces the static weather.json
file found in the Client-Side template.
public class SampleDataController : Controller
{
// ...
[HttpGet("[action]")]
public IEnumerable<WeatherForecast> WeatherForecasts()
{
// ...
}
}
Nearly all of the Client application is identical to that of the Client-Side template. However, in FetchData
example differs slightly by requesting data from the WeatherForcasts
web API endpoint in the GetJsonAsync method call.
@functions {
WeatherForecast[] forecasts;
protected override async Task OnInitAsync()
{
forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
}
}
Since the project includes a server and client solution that both use .NET it's possible to share code between both applications. This is a scenario unique to Blazor since the client is running .NET client side instead of JavaScript. The example given in the project template utilizes the same WeatherForecast
class on both the server and client application.
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
The WeatherForcast
class is just a basic idea of shared code, however other shared application code may include: validation, converters, and business logic which is decoupled from system, IO, or web concepts. Expanding on this idea further a theoretical application may share libraries between other .NET frameworks such as: Xamarin, Windows Universal Platform (UWP), or other .NET based web applications.
In other .NET web technologies like ASP.NET MVC (and Core MVC), the Razor templates are rendered by the server and sent to the client as HTML. Some JavaScript frameworks like Angular and React share rendering responsibilities on both client and server in a process called ahead-of-time Compilation (AOT) or Isomorphic Rendering. Even though the Blazor client application is hosted on a .NET server, all views are rendered client-side in both the Client-Side and Full-Stack project templates. Currently, there are no AOT/Isomorphic Rendering options for Blazor, but a Server-Side Blazor app model does exist.
On September 12, during DotNetConf Blazor Server-Side was announced as a new feature in ASP.NET Core 3.0 and will be known as ASP.NET Razor Components.
ASP.NET 3.0 Razor Components will be the first official (non-experimental) release of the Blazor framework.
The Blazor Server-Side project template takes a significantly different approach to how a Blazor application is delivered and interacts with the browser. When using the server-side configuration Blazor utilizes the browser as a "thin-client" by deploying a SignalR JavaScript application to the client. On the server, Blazor implements a SignalR hub communicating with the client via web sockets. In the server-side hosting model, Blazor is executed on the server from within an ASP.NET Core app. UI updates, event handling, and JavaScript calls are handled over the SignalR connection. In this configuration there is no need for WebAssembly and Blazor is executed on the ASP.NET Core runtime at the server. All UI updates are sent as diffs, bidirectionally as binary packets over web sockets. To the user, the application is indistinguishable from any other web application.
Despite the drastic differences in how Blazor operates server-side, the actual application model stays relatively the same. In the server-side project template there are a only few differences in example code provided by the template. The template includes two projects: a Server-Side Blazor application App
, an ASP.NET Core server application Server
which hosts the Blazor app.
The index.html
is the client entry point to the application. When configured for server-side operation the JavaScript file, blazor.server.js
replaces blazor.webassembly.js
. blazor.server.js
invokes the SignalR client and establishes communication with the Blazor server-side application.
<!-- wwwroot/index.html -->
<!DOCTYPE html>
<html>
<head>...</head>
<body>
<app>Loading...</app>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
Because the entire application runs server side, in this project type the Fetch Data example utilizes data from a local service. The @inject
directive in this example resolves an instance of WeatherForecastService
in place of HttpClient
as seen in the Full-Stack project template. The WeatherForecastService
in the example is a simple class that generates random data, however in a real-world scenario the service could be an Entity Framework database context, repository, or other sources of data.
@page "/fetchdata"
@inject WeatherForecastService ForecastService
// ... markup omitted for brevity
@functions {
WeatherForecast[] forecasts;
protected override async Task OnInitAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
// ...
}
The WeatherForecastService
and other services are added to the dependency injection container in the ConfigureServices
method found in Startup.cs
.
public void ConfigureServices(IServiceCollection services)
{
// Since Blazor is running on the server, we can use an application service
// to read the forecast data.
services.AddSingleton<WeatherForecastService>();
}
The server project provided by the template is a simple ASP.NET Core host. In the Startup.cs
of this project, the UseServerSideBlazor
middleware is invoked and the Blazor application's startup is initialized.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseResponseCompression();
// ...
// Use component registrations and static files from the app project.
app.UseServerSideBlazor<App.Startup>();
}
Since only a small amount of JavaScript is required to bootstrap the client and no .NET assemblies are transferred to the client the Server-Side Blazor application is efficient with network traffic. Even during operation network traffic is light because communication between the browser "thin-client" and the server is a small binary packet. However, because web sockets are used, the client and server must always be connected. This means that Blazor Server-Side apps cannot work in offline mode.
While Blazor is an extremely new framework still in its experimental phase it ships with several ways to get started. Each project type includes a similar set of examples with the Counter and Fetch Data components. The Fetch Data example varies from project to project to showcase specific features of that project type.
With the Client-Side, Full-Stack, and Server-Side project templates developers can choose the starting point that best fits their application's requirements. The Client-Side template focuses on running static files completely in the browser, while the Full-Stack template includes ASP.NET Core hosting and Web API. Using the Server-Side template utilizes the Blazor framework on the server and relies on SignalR in place of WebAssembly thus trading performance for a dependency on always being connected.
If you're excited about Blazor and are ready to begin experimenting too let us know in the comments below. What are your plans for Blazor?
Ed