February 2019
Volume 34 Number 2
[Cutting Edge]
Dealing with Forms in Blazor
By Dino Esposito | February 2019
ASP.NET Core Blazor is a C#-based, client-side framework to build single-page applications (SPAs). In this regard, it’s not much different from Angular, in that it can interact with whatever back end you may have that exposes HTTP-reachable endpoints. At this time, however, Blazor is a work in progress and far from go-live.
That said, some parts of Blazor are shipping as part of ASP.NET Core 3.0. Razor Components is a special configuration of a Blazor application that runs entirely on the server, side-by-side with a classic ASP.NET Core application that might constitute its back end. The coexistence of a classic Web application mostly used for back-end tasks and a distinct Blazor-based presentation layer on the ASP.NET server simplifies some programming scenarios, and establishes a sort of middle ground between a pure SPA approach and a pure server approach. In this article, I’ll discuss how to deal with input forms and client-to-server communication. Let’s start with a quick survey of Razor Components.
Server-Side Blazor
Blazor is a core framework designed to receive and handle events regardless of the surrounding environment. Today, the only fully supported scenarios are running Blazor within the UI thread of a hosting browser and within the ASP.NET Core runtime. Other realistic scenarios yet to come are running Blazor within a Web worker or even within some desktop-enabling platforms such as Electron. Hosting in a Web worker would make Blazor a suitable platform for progressive and offline Web applications, while hosting in a platform like Electron is also reasonable. As an example, let’s briefly compare the instructions for the very basic Electron startup (see https://github.com/electron/electron-quick-start) with what happens when a new server-side Blazor project runs.
The Electron’s quick-start source code contains the following method:
function createWindow () {
mainWindow = new BrowserWindow({width:
800, height: 600})
mainWindow.loadFile(‘index.html’)
}
The method is responsible for creating the browser window and for loading a separate HTML file into it. All that happens next is a continuous exchange of messages between the outside world (the browser) and the innermost shell (the HTML pages). The framework that sits in between just manages the burden of proxying messages appropriately. Let’s see what happens when a server-side Blazor application runs, as depicted in Figure 1.
Figure 1 The Loading Phase of a Server-Side Blazor Application
First, the browser loads the application-level index.html file that defines the main page layout (mostly the HEAD tag) and, as part of the download, also loads a small JavaScript file named blazor.server.js. The _blazor/negotiate step in Figure 1 indicates the moment an ASP.NET Core SignalR connection is established between the browser’s host environment and the Blazor app. Finally, the connection is upgraded to Web Sockets. This happens in the final step captured by the Fiddler screen in Figure 1.
In both cases, the host environment loads up an initializer file. Once the connection is established, the application logic runs and produces any required HTML. In server-side Blazor, the HTML produced server side is compared to the currently rendered DOM and only the necessary fragments of the DOM are actually updated. All updates run over the SignalR connection.
User clicks captured within the browser go the other way around. Related events are packaged up and sent server side, where they’re processed by the Blazor JavaScript interop layer and processed in C# code.
Visual Studio comes with an ad hoc project template for server-side Blazor applications. The typical project includes a client project and an ASP.NET Core project. Most likely, you’ll want to add a .NET Standard library project to hold classes that must be shared between the front end and the back end.
Setting Up the Client App
The startup class of the client application is nearly empty:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IBlazorApplicationBuilder app)
{
app.AddComponent<App>(“app”);
}
}
The only necessary change is injecting a reference to HttpClient so that any of the Blazor pages will be able to place HTTP remote calls:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<HttpClient>();
}
The most relevant addition to the client application is a component that allows users to fill out and post a form. In Figure2 you see the application UI produced by the Register User button. It’s a plain simple HTML form that collects an e-mail address and password from the user and passes it back to a remote back end.
Figure 2 The Sample Form
The interesting thing here is the markup you need to generate the container for the input fields in Figure 2. Even though the container walks and quacks like an HTML form, it’s not a true HTML form, as shown in Figure 3.
Figure 3 Markup to Produce a (Non-HTML) Form
<div class=”col-sm-6 col-md-4 offset-md-4”>
<h3 class=”text-center login-title”>I Want To Be a New User</h3>
<div class=”account-wall”>
<div class=”form-signin”>
<input type=”text”
class=”form-control”
name=”email” bind=”@Email” />
<input type=”password”
class=”form-control”
name=”password” bind=”@Password” />
<button class=”btn btn-primary
onclick=”@TryRegister”>
Sign me in
</button>
</div>
<div class=”text-center”>@Message</div>
</div>
</div>
As you can see, the markup is pure HTML, but there’s no need for a FORM element. In the end, the C# code running on the client will take care of collecting the input values and will forward them to a remote endpoint that might (or might not) be an ASP.NET back end.
The bind attribute you see associated with the INPUT elements guarantees bidirectional binding between the pseudo-form and the properties of the Blazor client page. The TryRegister function is responsible for posting the content to the back end. Figure 4 shows the code of the client page’s @functions block.
Figure 4 The @functions Block
@inject HttpClient Http...
@functions
{
public string Email { get; set; }
public string Password { get; set; }
public string Message = “?”;
public async Task TryRegister()
{
var input = new RegisterUserViewModel(Email, Password);
try
{
var response = await Http.PostJsonAsync<CommandResponse>(
“.../account/register”, input);
if (response.Success)
Message = response.Message;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
The content of the input fields are gathered in a fresh instance of a data-transfer object—RegisterUserViewModel—and serialized as JSON through the PostJsonAsync method of HttpClient. The back end receives an HTTP POST request, in which the body is set as follows:
{“email”:”...”,”password”:”...”}
In a server-side Blazor application, the back end is usually an ASP.NET Core application. In this case, the request is forwarded to a controller endpoint where model binding applies. As a result, the posted data will be captured by a server-side instance of the same RegisterUserViewModel class. Note, though, that the back end doesn’t have to necessarily be an ASP.NET Core application. The URL you specify in the call via HttpClient must be an absolute URL that can post data to virtually anywhere, including some legacy Visual Basic-based back end.
Setting Up the Server App
In the sample application, the server application is a plain ASP.NET Core Web API, secured as you would secure any such Web API. For example, it might be protected against unauthorized access via a JWT token that would work well also with non-Web clients, including desktop and mobile clients. For now, let’s just assume that the Web API allows anonymous access. Here’s the code you need to successfully receive and process any Blazor-posted data:
public class AccountController : Controller
{
[HttpPost]
public IActionResult Register(
[FromBody] RegisterUserViewModel input)
{
// Some work here ...
return Json(CommandResponse.Ok.AddMessage(“Done”));
}
}
The FromBody attribute plays a key role here. Without it any call made toward the endpoint, the way the Blazor client does it, would throw a model-binding exception.
The FromBody attribute instructs the ASP.NET Core runtime to bind the parameter to the data found in the body of the incoming HTTP request. Note that there can be at most one parameter per action decorated with the FromBody attribute.
ASP.NET Core delegates the responsibility of processing the request stream to a dedicated formatter class. The default class is JsonInputFormatter. Note also that once the request stream has been read for a parameter, it can’t be read again for another parameter. The stream pointer, in fact, has advanced to the end of the body and can’t be moved back.
The response from the Web API is another JSON response that the Blazor client receives and deserializes, like so:
var response = await
Http.PostJsonAsync<CommandResponse>(absoluteUrl, input);
In this code snippet, the response is deserialized to an instance of the CommandResponse type and an exception is thrown if it turns out to be impossible.
A Word on Security and Authentication
The client part of Blazor runs in a sandbox in the same way that JavaScript code does. The server back end is completely disconnected from the client and reachable only at the conditions the back end defines. The Blazor client app must comply with whatever security layer you have in place around the back end. In this regard, Blazor is a plain Web-based client that calls into a Web API. It may authenticate through a cookie, but more likely it will need to authenticate through a JWT bearer token.
The server-side Blazor scenario may be a different story, in the sense that it may enable alternate scenarios for authentication and authorization. No decision has been made yet on this point, though you can get up to speed on the discussion at bit.ly/2CdS74c. Chances are that some built-in API will be provided to streamline the creation of login and logout forms for server-side Blazor applications, as well as for other common authentication operations such as recovering a password and interacting with the internal membership system. It’s possible that Blazor will in some way incorporate the built-in UI of ASP.NET Core Identity. But again, nothing is decided at the time of this writing.
Pros and Cons of Razor Components
Server-side Blazor, or Razor components, will be the first part of Blazor to reach go-live status when it ships as part of ASP.NET Core 3.0. Some developers may balk at the drawbacks. Used to set up a pure SPA solution, Blazor may seem too distant from the programming experience of classic ASP.NET Web developers. Also, WebAssembly is required to run C# within the browser, which imposes a cost in terms of download and initial startup, while debugging can be problematic. With a server-side solution, the development experience is smoother, with improved debugging, JIT compilation and a larger API. What’s more, all browsers are supported, regardless of their native support for WebAssembly. For a classic ASP.NET developer, refreshing only parts of the UI becomes nearly as easy as in a desktop application.
The issue is that all this magic relies on a SignalR connection. This brings up a few potential problems. First, there will be quite a number of requests going over the wire. Any updates, in fact, require a roundtrip. Second, the server is forced to track every client and its state. ASP.NET Core SignalR is also offered as an Azure service—and this provides a great foundation for scalability—but nobody has tried it out yet in the real world. Finally, there’s no built-in mechanism yet to restore the application state in case the SignalR connection drops. Any persistent solution is good, but it’s all left to developers for now.
All in all, Blazor promotes the SPA pattern and the SPA pattern is a bit disruptive for many developers. Server-side Blazor (aka, Razor Components) is a smart, non-disruptive way to move piecemeal toward the SPA architecture. The source for this article is based on Blazor 0.7.0 and is available at bit.ly/2EpGF8d.
Dino Esposito has authored more than 20 books and 1,000-plus articles in his 25-year career. Author of “The Sabbatical Break,” a theatrical-style show, Esposito is busy writing software for a greener world as the digital strategist at BaxEnergy. Follow him on Twitter: @despos.
Thanks to the following technical expert for reviewing this article: Jonathan Miller