Cloud

In this post, I want to explain how adding an UI build in Blazor in our ASP.NET Core applications based on clean architecture principles. In particular, I want to write this as an extension of how to create a digital transformation in your company.

The source code of this post is on GitHub. Because one single post is too long, I have created the following posts:

All right, we have a tested API. Time to really use the API now. In this post, we’ll be adding a UI to the solution. I want to show you how adding an UI built in Blazor using the API for real, and while doing so, we’ll learn about some universal ways to integrate with our API in client‑side technologies.

The UI technology I’m going to be using here is Blazor. If you don’t know Blazor yet, I will give you a very high‑level overview of the technology and some pointers on where to learn more. Next, we’re going to learn how a large chunk of the client‑side code that communicates with the API can be generated, and we’ll use for that NSwag and NSwagStudio.

Next, although it’s not the focus of this course, I want to take you through the client‑side code without going into specifics of how the Blazor code can be written. Finally, I’m expecting another change we’ll need to work through.

Introducing Blazor Client-side

Before we start, I’m going to give you a brief, high‑level overview of Blazor. So, we’ll use client‑side Blazor, also known as Blazor WebAssembly. So what exactly is Blazor than? Well, great question. Blazor is the technology from Microsoft that allows us to write interactive UI for the web using HTML and C#. Is it like MVC or Razor Pages then? Well, no. MVC and Razor Pages are server‑side technologies, but Blazor is a client‑side technology, and thus allows us to create apps that run client side natively in the browser.

So, the model is the same as what you would create with Angular, Vue or React, but the main difference is that you are now not using JavaScript. Instead, you’re writing C# and HTML. If you’re not so much into writing JavaScript, but still want to create client‑side in‑browser apps, Blazor is a great choice.

When creating Blazor applications, we actually have two options of how we want to run the code. This model is what is known as client‑side Blazor, and it’s built on top of WebAssembly. Alternatively, the exact same code can also be run on the server. I’ll explain the differences some more in just a minute. Before understand how adding an UI built in Blazor, some more concepts.

Client-side technology

As mentioned, Blazor is based on WebAssembly, which modern browsers supports natively. Therefore, to run a Blazor application in the browser, the user doesn’t need to install anything. Just a modern browser will do fine. No need to install extra plugins.

Blazor is very extensible and can in fact integrate with already existing JavaScript components. So, anything that is not supported in Blazor yet can still be created in JavaScript and can then be used directly from Blazor. So, Blazor is entirely based on C# and .NET. Then, you don’t need to learn a lot of new things. All the typical packages you know, all the .NET namespaces, all that transitions to Blazor.

You write Blazor apps Visual Studio as well. Again, nothing new to learn. It’s pretty easy to start with if you’re already familiar with C# and .NET. Blazor has a very cool feature. We can run the same code in different modes. Let me explain.

When we talk about client‑side Blazor, we’re talking about the model where things are running using WebAssembly. In this case, your compiled Blazor application is just a file which lives on a server, and when someone browses to the application, the application is downloaded entirely to the client side and runs from there.

Server-side

Even if you lose the connection, things will keep running fine since it’s running entirely on that client as an in‑browser app. Now, if you want, you can also run the exact same code on the server. In this case, there will be an ASP.NET Core server‑side app that hosts the Blazor app.

The application that was running with client side on the client will now run on the server. When the user browses to the app, the initial view is downloaded, and end user will see that. Immediately, a connection is opened up using SignalR.

When the user now interacts by clicking on something in the UI, information about that event is sent to the server using SignalR. The running application will get in this information, executes code, and the resulting changes that need to happen to the DOM are sent back to the client. The browser will then reflect these changes. The cool thing is that it is the same code as we have with client‑side Blazor.

Basically, you run the same code in the different models. We will be using client‑side Blazor. The reason I’ve chosen that here is that you can hopefully see that this is then also using the same model as any typical JavaScript‑based application written in Angular, Vue, or another framework.

The way we interact with the back end is what we need to learn about. Of course, there’s much more to learn about Blazor than what I have just explained.

Using NSwag and NSwagStudio

Like I said, the goal is showing you how we can interact with the API that we have so neatly set up using clean architecture, and that’s what we’ll do here. What I use first is NSwag and NSwagStudio studio, two building blocks that make the consumption off the API that we have created a lot easier in just any client. We have our API. It’s a REST API that we’ve built with C# and .NET Core. But for the outside world, what we’ve built it with is of no importance.

Since it’s a REST API, it returns JSON when we send in the request. We’ve added also, using Swashbuckle, the full description of the API, both in a machine‑readable and a human‑readable format. So, now that we have moved on to the client side, we now need to write code that uses the API.

That means plain HTTP code that will build up the request, sends it, waits for a response to come back, and once received, will process it. In many cases, that’s a lot of boilerplate code that needs to be written in each client‑side technology that’ll access the API. I will need to write this in C# to use my API in Blazor. But maybe someone else is using the same API from an Angular app, and so they will be writing the same functionality in TypeScript.

Can’t we have a lot of this in an automated way? We have, after all, already made the Swagger endpoints available, right? Well, there is another open source project that is a very commonly used for this, and it’s called NSwag.

NSwag

NSwag is a toolset which can generate a lot of the boilerplate code that we need to interact with our API. It will do this based on the Swagger spec of the API, so we will basically point it to the JSON endpoint that describes the API and its methods.

Using NSwag, all the code to send requests will then be generated. Also, the types required for this to work will be generated for the client side. So, in our case, the return types that we’re getting back from the API, as well as the types required to send our commands, such as add category, will be generated. That’s a lot of manual work we don’t need to do anymore. And the good news is that this works for C# and TypeScript.

So, no matter if you’re building a Blazor app where you’d use C# or Angular, Vue, or React app where you’d use TypeScript, it’ll work for both. Basically, this is what we’ll get. On the left here, we have our API, and it exposes a Swagger endpoint. In our case, we’ve set that up before.

The team that works on the client projects will now use NSwag to generate, based on the specification of the API, the client‑side boilerplate code, for C# or for TypeScript. The generated code will include all the code for interacting with the API, as well as the types, so the classes to use on the client side.

NSwagStudio

Now, how do we then generate this code then really? Well, there are a few options that work all pretty much in the same way. What I’ll be using is NSwagStudio, another open source tool, which is basically a GUI for NSwag. It comes with many options to tweak the generated code. You can also have the code be generated as part of the build or using a CLI.

Here you can see a screenshot of its interface, and you can clearly see that the focus lies in the options that you, as a user, will get to influence what code will be generated.

Generating Client Code Using Nswag

In the first demo of this module, I’m going to connect with NSwagStudio to our running API and let it generate code that we’ll then use in the next part in the Blazor app. So, NSwagStudio is a desktop application, and you can just get it from GitHub. Simply search for NSwagStudio, and you’ll get this client‑side application.

NSwagStudio on GitHub - How adding an UI built in Blazor
NSwagStudio on GitHub

This is NSwagStudio running, and the first thing I’m going to do is, of course, let it know about my JSON file, so my swagger.json file. So with my application running, this is the swagger.json link, and I’ve copied that here into NSwagStudio.

Use swagger.json

Then, I clicked on Create Local Copy, and then it reads out the JSON file. In our case, then we click on C# Client. But, of course, you can also have it generate TypeScript code if you’re using your API from a JavaScript‑based application. And I’ve added a couple of more settings.

Configuration for NSwagStudio for CSharp Client Settings - How adding an UI built in Blazor
Configuration for NSwagStudio for CSharp Client Settings

So, I’ve specified the namespace that I want the generated code to be part of. I’ve specified that I want to have it generate client classes. I’ve also specified here that I want to inject the HttpClient using the constructor. That will enable me from a Blazor application later on to pass in the HttpClient to this generated code so that I can also use the HttpClientFactory, which is the preferred way of doing this. I’ve also asked it to create the DTO types.

Inject HttpClient in the NSwagStudio settings - How adding an UI built in Blazor
Inject HttpClient in the NSwagStudio settings

Now, those are the classes that it will generate for the client side. Finally, all the way at the bottom, this is the place where my generated class, the ServiceClient class, is going to be generated. So, that’s directly in my Blazor application.

If I click Generate Files, it will regenerate this file. Every time that you make a change to your API so that the change appears in this regular JSON file, click on Create Local Copy to read in the new JSON file, and then click on Generate Files, and it will regenerate your client‑side code, so your C# code in our case.

Generate code

On my repository on GitHub, this Nswag file is also included so that you’ll already have all the settings that I’m using available to you. Let’s take a look at the generated code. So, that lives in the Blazor project, and we’ll take a look at the Blazor project in just a minute.

First, let us take a look at what was generated. So, in here in the Services, Base, we have this ServiceClient class. So this is the generated code. It’s quite a lot of code. It’s even more than 1500 lines of code. That is all generated for me. Now what is in here? Let’s take a look.

Generated code

Generated code from NSwagStudio in the Visual Studio project - How adding an UI built in Blazor
Generated code from NSwagStudio in the Visual Studio project

The most important one probably is the IClient. That’s the interface that has all the methods that we have defined on the API, and you see already a few here that we haven’t looked at, Authenticate and Register. We’ll take a look at those in the next module. But you see also our GetAllCategoriesAsync, which has been generated.

Next, to all the methods, you also see that the types that we have, for example, the EventListVm, that’s a type that we’ve used quite a few times that contains the eventId, name, date, and imageUrl. Those have also been generated inside of our generated code from NSwagStudio.

Of course, all of this has been generated also including the code that will make the actual API call that lives in the client class. The client implements IClient, the interface, and that expects an HttpClient. And that’s important because I’m going to use that to be able to inject my own HttpClient into this generated code.

In here, we’ll find all the methods that we already saw. So, for example, the AuthenticateAsync. Let’s not look at that one. Here you see, for example, the GetAllCategoriesAsync, which is then going to make the call to api/category/all. All of this code, we don’t need to write it ourselves. It’s already been generated for us, so we can now use this.

How am I now using it in my application?

In the Program class, I’m registering the IClient, which is the interface generated by NSwagStudio, with the client. And so that’s our generated class. In the service collection, you can see that I’m registering this here using the AddHttpClient, which is using the IHttpClientFactory. And that’s the preferred way of using the HttpClient from a Blazor application or, in general, from just any .NET Core application that uses an API from code.

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.RootComponents.Add<App>("app");

        builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
        builder.Services.AddBlazoredLocalStorage();

        builder.Services.AddAuthorizationCore();
        builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();


        builder.Services.AddSingleton(new HttpClient
        {
            BaseAddress = new Uri("https://localhost:5001")
        });

        builder.Services.AddHttpClient<IClient, Client>(client => client.BaseAddress = new Uri("https://localhost:5001"));

        builder.Services.AddScoped<IEventDataService, EventDataService>();
        builder.Services.AddScoped<ICategoryDataService, CategoryDataService>();
        builder.Services.AddScoped<IOrderDataService, OrderDataService>();
        builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();

        await builder.Build().RunAsync();
    }
}

That’s what we are doing here. I’m configuring here also the BaseAddress. Let’s now see how we are going to use this from Blazor application code. This is not really specific to Blazor. I have created really service classes in this case inside of my client app that will talk, that will communicate, with that service client.

CategoryDataService explained

Let’s take a look at, for example, the CategoryDataService. The CategoryDataService inherits from the BaseDataService. The constructor accepts an IClient, an IMapper. We’re, again, using AutoMapper and a local storage server to store data locally.

Then, most importantly in the GetAllCategories, we are now calling on the client, that will be the client of the ServiceClient class, the generated code. I’m simply calling onto that the GetAllCategoriesAsync, and that lives in the generated code.

That will then return me simply all categories without me having to write code that goes out to the API, creates the Get request, waits for the response. All of that, I don’t need to do. I simply call the GetAllCategoriesAsync that was generated for me using NSwagStudio.

The return data is of type CategoryListVm. That’s the type that was returned from my API and also was generated in the generated code. Then, I map that back to, in this case, a ViewModel using AutoMapper, and that I’m then returning to the caller, which will be the UI, and we’ll take a look at that in just a minute.

As you can see, using NSwagStudio, we don’t have to write all that code manually to interact with our API because we exposed a Swagger endpoint on the API, and I let NSwagStudio look at that endpoint, generate code, while I’ve saved myself a lot of time.

Exploring the Client App

Now, that we can save ourselves a lot of time, we’re generating all the code to interact with the API, let’s now take a look at how we can use this from our Blazor application. And while we’re at it, we’ll look in a bit more detail at the Blazor app itself.

Now, before doing so though, there are a few points I want to highlight here. In the Blazor app too, we’ll make use again of some packages. I’ll be using AutoMapper again to map between the different types. This is what I love about Blazor. It’s the same tech on both sides of the spectrum. Things that we’ve learned can easily be applied here too.

For the communication with the back end to the REST API, we’ll use the HttpClientFactory, not the regular HttpClient. I said we’ll include the code that was generated with NSwag, so that’s the actual code that’ll be communicating with the REST API. We’ll also bring in some authentication, a basic form that is.

Exploring the Blazor Application

Now, we’ll take a look at the Blazor app and see how it integrates with the API. So by means of example, I’m going to show you how one screen is built, and they’re all pretty much the same. I want to show you a bit of the Blazor code and how it actually talks with our back end.

Run the application

Let’s click on Events, for example, and we’ll then see a list of all events. I think you can already imagine which column we are going to be using for that. Let’s take a look at the code. The page that you just saw is this EventOverview, and it has a code‑behind file. The EventOverview is just Blazor code and is actually going to loop over a list of events that we’re going to expose, and it’s then going to visualize all these events in the table that you just saw.

List of events in MyTicker application
List of events in MyTicker application

Now, I’m not going to explain you everything that has to do with Blazor. This is pure Blazor code. Again, take a look at that other codes that I mentioned. But let’s see how this is now working together with what we already saw, that code that was generated with NSwagStudio. So this is the class code‑behind that contains the functionality.

<table>
    <thead>
        <tr>
            <th></th>
            <th>Event name</th>
            <th>Event date</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var ev in Events)
            {
        <tr>
            <td><img src="@(ev.ImageUrl)" class="event-image" /></td>
            <td class="event-name">@ev.Name </td>
            <td class="event-date">@ev.Date.ToShortDateString() </td>
            <td>
                <a href="@($"eventdetails/{ev.EventId}")" class="event-purchase-button btn">
                    <i class="fas fa-edit"></i>
                </a>
            </td>
        </tr>
            }
    </tbody>
</table>

EventDataService

The EventOverview will use an EventDataService. That is one that also lives here under Services, which is going to work again with the generated code available through the client. In the GetAllEvents, I’m simply going to call the GetAllEventsAsync, which is going to be in that generated code and determine the list of all events in an asynchronous way, that is. That returns me, again, the EventListVms, and I’m going to map that to an EventListViewModel, a local type inside of the Blazor application. I’m using AutoMapper for that.

public partial class EventOverview
{
    [Inject]
    public IEventDataService EventDataService { get; set; }

    [Inject]
    public NavigationManager NavigationManager { get; set; }

    public ICollection<EventListViewModel> Events { get; set; }

    [Inject]
    public IJSRuntime JSRuntime { get; set; }

    protected async override Task OnInitializedAsync()
    {
        Events = await EventDataService.GetAllEvents();
    }

    protected void AddNewEvent()
    {
        NavigationManager.NavigateTo("/eventdetails");
    }

    [Inject]
    public HttpClient HttpClient { get; set; }
}

Again, we have defined profiles here, which are going to do the mapping between the different types so that AutoMapper knows between which types it needs to map. So I’m returning that list of EventListViewModels from this EventDataService. So back to the real Blazor code, that EventDataService is being injected.

Blazor and dependency injection

In Blazor, we are using also dependency injection, but I cannot use constructor injection in a component. So this is actually a Blazor component. The EventDataService, as you can see here in the startup, Blazor doesn’t use a startup, but uses a program instead. But I am indeed using here the EventDataService, and I’m registering that with the service collection.

Then on that EventDataService in the OnInitializedAsync method, which is going to be called when the page loads and the component loads, I’m going to call that GetAllEvents method, and that’s the one I just showed you. That will fill up this collection of EventListViewModels, and that is the one that I was actually binding to in the UI. So that’s how I’m listing out the events in my UI.

Now this should already give you an idea of how the Blazor application is really built up. We are using components, pages in this case, and those are using our data services, which are working with our generated code to talk with our API.

Adding the Paging Functionality End-to-end

The page that lists out the orders is now pretty long, can we add paging to that? Of course we can Mary. In the final demo of this module, we’re going to bring in another feature end‑to‑end, namely paging in the oldest page. For this we’ll also need to rerun NSwag, and we’ll then need to update our Blazor code. I’m going to show you that adding a new functionality doesn’t have to be a big problem. We can actually pretty easily bring in a new functionality. I’m already showing you the result.

Initially, we had a very long list of sales, and I think that’s pretty easy to understand, and I don’t think it’s a good user experience to have a long list of data. Instead, what we should be using is paging, so I have now added functionality for paging onto our API and also in the Blazor app. You can see that I can now page to the list of sales.

How did I bring that functionality in?

Let me take you through the steps I’ve gone through here. On the API, and that would be on the OrderController, I have now added a method, GetPagedOrdersForMonth, that is expecting a DateTime, so the month, but also the page and the size. The page will contain, well, the page int that is requested in the UI and size will point to the number of orders I want to return. I have created then in my application code a new GetOrdersForMonthQuery.

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private readonly IMediator _mediator;

    public OrderController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpGet("/getpagedordersformonth", Name = "GetPagedOrdersForMonth")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesDefaultResponseType]
    public async Task<ActionResult<PagedOrdersForMonthVm>> GetPagedOrdersForMonth(DateTime date, int page, int size)
    {
        var getOrdersForMonthQuery = new GetOrdersForMonthQuery() { Date = date, Page = page, Size = size };
        var dtos = await _mediator.Send(getOrdersForMonthQuery);

        return Ok(dtos);
    }
}

GetOrdersForMonthQuery explained

Indeed, I’ve added under Orders a new query that also expects the date, the page, and the size. In the Handler, I have now added on the orderRepository a new method, GetPageOrdersForMonth, which expects the DateTime, the page, and the size, but now on the repository level, so that in the orderRepository, in the actual implementation in the Persistence project, we can now go to the dbContext and update the query so that only the right orders are returned.

That’s what you see here. But notice while we’re here, I have also added another method, GetTotalCountOfOrdersForMonth, because my Pager will also need to know how many orders that will be, so I have added that here also on the repository. Indeed, in the Handler I’m also asking the Repository for the total count of orders for that month.

Then, I’m going to return a new VM, PagedOrdersForMonthVm, which is going to contain the orders as an ICollection of OrdersForMonth, but also the count, so that will be the object that I’m going to return to my client.

Pagination

So, indeed, if we look back at the controller, that’s going to send that GetOrdersForMonthQuery, and it’s going to send that to MediatR, and that is going to return a PagedOrdersForMonthVm. My client then not only has the orders for that month, but also knows how many orders there are in total that it can use in the Pager control, and is included in the Blazor application to show the number of pages, as well as the list of orders.

Now I’ve added this new method on my API, so I also need NSwag to run again. I have already shown you I need to have NSwag regenerate, let’s say, that client‑side code. Once that is done, that code is updated in the ServiceClient. In the OrderDataService, I now have the GetPagedOrdersForMonth method that I’ve added. That will call that method that is generated in the client and returns the PagedOrdersForMonthVm that I’m mapping to a local type, again, using AutoMapper, that is then being returned.

If we then look back at the TicketSales page, we indeed see that we are using again a table to show the orders, and we also use a custom control, a Pager that will list out the number of the pages, as well as the active page. The TicketSales component code will use the OrderDataService, and will go in its GetSales method to the OrderDataService, asking it for the GetPagedOrdersForMonth, passing in the month and year I want the sales for, the page number, and the page size. So the key message here is, it’s the same story. We just add a functionality on our API, we add a new query in our application code, we implement the new functionality on the repository, and we call that from our Blazer application after we have run again NSwagStudio to generate that code that talks with the API.

By Enrico

My greatest passion is technology. I am interested in multiple fields and I have a lot of experience in software design and development. I started professional development when I was 6 years. Today I am a strong full-stack .NET developer (C#, Xamarin, Azure)

7 thoughts on “How adding an UI built in Blazor”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.