Blazor Vs. Razor
Akash Limbani
CEO & Co-Founder at Cogtix | Leading Strong High-Performance Teams | I equip Engineering Leaders with cutting-edge knowledge in Software Product Engineering, Cloud Development, AI, and all facets of technology.
If you have worked with ASP.NET MVC in the past, then you have probably interacted with Razor Views in one way or another. Since ASP.NET MVC was introduced in March 2009, Razor has been the View in Model View Controller. But what is all this talk about Blazor recently? How are these two frameworks related? Is Blazor going to replace Razor? In this post, we will take a look at both of these frameworks, what they are and how they can help you build your next web application.
BUILDING A RAZOR APPLICATION
Lets get started by creating a new ASP.NET Core Web application. Selecting the Web Application (Model-View-Controller) will generate a traditional MVC application with Razor as the view engine.
For detailed instructions, you can follow the step-by-step tutorial here.
Once you select OK, Visual Studio will generate a basic starter application. From here you can build, run, and debug your new project. As mentioned previously, ASP.NET Core MVC uses Razor by default. Lets zero in on what Razor Views are and the purpose they serve.
Razor View’s exist to create a user interface (or view) for the end user. In a web application, this consists of HTML and references to any required CSS and JavaScript files. ASP.NET Core MVC specializes in creating “dynamic” web applications. If the contents of the views were always static, there wouldn’t be a need for a view engine at all. This is where Razor works its magic.
Taking a look at the error view (~/Views/Shared/Error.cshtml), we can see an example of a typical view.
@model ErrorViewModel @{ ViewData["Title"] = "Error"; } <h1 class="text-danger">Error.</h1> <h2 class="text-danger">An error occurred while processing your request.</h2> @if (Model.ShowRequestId) { <p> <strong>Request ID:</strong> <code>@Model.RequestId</code> </p> } <h3>Development Mode</h3> <p> Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred. </p> <p> <strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
</p>
As shown in this example, when a controller returns a view it can optionally provide a view model. A view model is an instance of a class that contains data that can be used to render the view. On Line 1, we can see a view model (ErrorViewModel) is bound. Lines 9 and 12 show how we can embed C# inside of our view in order to provide logic and display data from our view model.
The result of this process is HTML. Once ASP.NET Core renders the view server-side, the HTML result is sent to the client. With our Error.cshtml view, lets take a look at the actual payload that gets sent to the browser.
<div class="container body-content"> <h1 class="text-danger">Error.</h1> <h2 class="text-danger">An error occurred while processing your request.</h2> <p> <strong>Request ID:</strong> <code>0HLK97BA1GHMF:00000004</code> </p> <h3>Development Mode</h3> <p> Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred. </p> <p> <strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application. </p>
Looking at line 7, we can see there is no sign of the @Model.RequestId that is present in our cshtml file. Rather, the Razor View replaces it with the actual contents of the RequestId property from our model, in this case 0HLK97BA1GHMF:00000004.
This highlights a key behavior of ASP.NET Core MVC applications. With each request, the server provides the browser with html and data. This concept has a couple other effects. Lets take a look at what happens when we browse to ~/Home/About and then ~/Home/Contact.
As we can see, with each request the browser receives a full set of HTML and in turn must request any additional JavaScript, CSS, images, etc it needs. In addition to the extra data going over the wire, the browser is required to fully render this new set of data.
SINGLE PAGE APPLICATIONS
Many of the limitations of server-side web frameworks like ASP.NET MVC led to the creation of Single Page Applications or SPAs for short. With SPAs, all views and UI logic is moved to the client. After the initial set of static content (html, js, css, etc), the only thing that is passed to and from the server is data, typically in the form of JSON. This is why these client side frameworks are commonly paired with a backend REST API.
In these types of architectures, user’s could browse from page to page with only the occasional API request back to the server. This leads to a much more fluid experience for the end users.
Due to the popularity of SPAs, there are many popular frameworks available to developers.
BUILDING A BLAZOR APPLICATION
While Single Page Applications provide us with a rich user experience, this does not come without a cost. A significant portion of the application gets moved to a new language with a different set of tools, frameworks, etc. This is where Blazor comes into play.
Blazor is designed to allow developers to build “Single Page Applications” with the same language (C#) on the client-side and server-side. Let’s get started by building a new Blazor application and take a look at some of the finer details. As before, we begin by creating a new ASP.NET Core Web application. The key difference this time is we select the Blazor project template.
Once complete, we will have the default Blazor project. Right off the bat, we will notice some differences. ASP.NET Core MVC leverages Razor Views and directly follows the Model View Controller pattern. Blazor on the other hand does not so, we will not see the same folders we did before.
Blazor uses Razor Components to render the UI. Components are similar to views in that they both leverage razor and have similar syntax. The key difference is views are dependent on a controller whereas components are not. Controllers have a tendancy to get bloated with actions. Components are designed to be more self contained and lightweight.
Looking at the Program.cs file however, we notice some striking similarities to our MVC project. The key difference is instead of the CreateHostBuilder function returning an IWebHostBuilder interface, we return an IWebAssemblyHostBuilder. The naming difference is not a surprise as Blazer uses Web Assembly in order to bring the power for .NET to the browser.
using Microsoft.AspNetCore.Blazor.Hosting; namespace JrTech.Web.Blazor { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) => BlazorWebAssemblyHost.CreateDefaultBuilder() .UseBlazorStartup<Startup>(); }
}
Similar to the IWebHostBuilder, the IWebAssemblyHostBuilder uses a Startup class to bootstrap the application. Like any ASP.NET Core application, the Startup class is used to register services and configure the application. By default, the App component gets registered in the Configure method.
using Microsoft.AspNetCore.Blazor.Builder; using Microsoft.Extensions.DependencyInjection; namespace JrTech.Web.Blazor { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IBlazorApplicationBuilder app) { app.AddComponent<App>("app"); } }
}
The App component’s (App.cshtml) sole purpose is to register an assembly where our Razor Components reside. Judging by the comment, a different approach will likely be implemented in the future.
<Router AppAssembly=typeof(Program).Assembly />
Unlike Razor Views, Razor Components directly contain the route. When a component with a @page directive is compiled, the generated class is given a RouteAttribute. This is similar to what you would see in a controller action.
You can observe this behavior in the Index component which also highlights the use of a child component.
@page "/" <h1>Hello, world!</h1> Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
The FetchData component is a little more interesting as it actually has a bit of functionality. Looking at the OnInitAsync function, we can see the use of the HttpClient which gets dependency injected into the view. It is important to stop and note what is interesting about all this. This Razor Component is using the C# HttpClient to call a backend API. Remember, unlike server-side Razor Views, Blazor runs in the client.
@page "/fetchdata" @inject HttpClient Http <h1>Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </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> } @functions { WeatherForecast[] forecasts; protected override async Task OnInitAsync() { forecasts = await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json"); } class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF { get; set; } public string Summary { get; set; } }
}
Not only are we using C# to make a Web request we are also binding the results to the UI. This gives us a lot of the same functionality you might find in a SPA framework however, we are utilizing the same language and framework as our Backend API! Let’s how this looks from the browser perspective and compare it with what we saw in our MVC application.
As you might expect, rather than just shipping HTML and JavaScript to the browser, we are sending DLLs. Web assembly is able to execute these binaries in order to give us the same rich functionality we are used to seeing with JavaScript. One interesting file to note is Mono.WebAssembly.Interop.dll. This shows us that Blazor is running on the Mono Framework.
Now before you delete all your Angular projects, it is important to mention that Blazor has not been released for production use yet. That said, it is clear that Microsoft is fully embracing this new technology. Razor Components will be included with the ASP.NET Core 3.0 release which is a huge step for this project. With Razor Components getting treated as a first class citizen, it is likely that Blazor will follow suit in the near future.
Now, Your turn.
Thank You :)
Senior Software Engineer | Backend | Microservices | Cloud | Tech Lead
4 年What I don’t like in blazor, we need to notify the component about content change. In angular we have no need to do the change detection manually for maximum time. But blazor required this frequently. Blazor use signalr. And because of that there are huge dependency to scale up an application.