To Do Rest API Using Asp.net Core – Blazor Client (Part 3)

In previous posts we created a simple but well documented Asp.Net Core Web API and I promised that we are gonna test out this API using several client side technologies, I have chosen to start with Blazor so we can explore this new framework.
Related Posts
To Do Rest API Using Asp.net Core (Part 1)
To Do Rest API Using Asp.net Core – Adding Swagger (Part 2)
To Do Rest API Using Asp.net Core – Blazor Client (Part 3) This One
To Do Rest API Using Asp.net Core – Angular Client (Part 4)
To Do Rest API Using Asp.net Core – Flutter Client (Part 5)
Blazor is new framework developed by Microsoft to create interactive client side web UI with .Net.
Tools
- Visual studio code
- Install the latest .NET Core 3.0 Preview SDK release.
- dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview6.19307.2 (Install via command prompt)
You could make use of visual studio 2019 preview to create a new project using the template but Blazor is still new and I have found some issues installing the Blazor Extension but you can give it a try.
Getting Started
Blazor offers two hosting templates, client and server side. For information on the two Blazor hosting models, server-side and client-side, see Blazor hosting models.
In this post I will start with client side hosting model, maybe in future posts we can develop another app using the server side template.
Creating a new blazor project
Head over to your cmd and navigate to directory you wish to place the project within. In your cmd type the following and hit enter to generate a new project.
dotnet new blazor -o ToDo.Blazor
Then navigate to the newly created project folder and run your project.
cd todo.blazor dotnet run
Now you should see something like this telling you the project url.
Hosting environment: Production
Content root path: D:\tmp\Blazor\ToDo.Blazor
Now listening on: http://localhost:5000
Now listening on: https://localhost:5001
Application started. Press Ctrl+C to shut down.
By default dotnet new creates the configuration for both HTTP and HTTPS to avoid errors and AntiVirus warnings you could open the HTTP version by pasting http://localhost:5000 in your browser and hitting enter. If you followed along you should see a sample App like the image below.

Model
Create a new Models folder in the root of your app, and inside add the following class.
namespace ToDo.Blazor.Models { public class ToDo { public int Id { get; set; } public string Description { get; set; } public string Status { get; set; } } }
Todo Page
Inside the pages folder add a new file called Todo.razor.
@page "/todo" <h1>Todo</h1> <ul> @foreach (var todo in todos) { <li>@todo.Description</li> } </ul> <input placeholder="Something todo" @bind="@description" /> <button @onclick="@AddTodo">Add todo</button> @code { private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>(); string description; private void AddTodo() { if (!string.IsNullOrWhiteSpace(description)) { todos.Add(new ToDo.Blazor.Models.ToDo { Description = description }); description = string.Empty; } } }
Todo Menu
Go to the NavMenu.razor file and add a new li for our Todo page like below.
<div class="top-row pl-4 navbar navbar-dark"> <a class="navbar-brand" href="">ToDo.Blazor</a> <button class="navbar-toggler" @onclick="@ToggleNavMenu"> <span class="navbar-toggler-icon"></span> </button> </div> <div class="@NavMenuCssClass" @onclick="@ToggleNavMenu"> <ul class="nav flex-column"> <li class="nav-item px-3"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <span class="oi oi-home" aria-hidden="true"></span> Home </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="oi oi-plus" aria-hidden="true"></span> Counter </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="fetchdata"> <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="todo"> <span class="oi oi-task" aria-hidden="true"></span> ToDo </NavLink> </li> </ul> </div> @code { bool collapseNavMenu = true; string NavMenuCssClass => collapseNavMenu ? "collapse" : null; void ToggleNavMenu() { collapseNavMenu = !collapseNavMenu; } }
Rebuild and Run
Press Crtl C to stop the server. Then rebuild and run again.
dotnet build dotnet run
Now refresh your page in the browser and navigate to the Todo page, try adding new Todos and see how the <ul> is populated with new Todo <li>s
Until this point we are still not interacting with our web api, if you refresh the browser your Todo list is cleared, next I will show how we can persist data to our back end and load Todos from our API.
Working a Web API
Before we implement the razor code inside our Blazor project there is something we need to modify in our back end web API.
CORS
In short Cross-Origin Requests (CORS) is the ability to request resources on a different URL or Domain than yours, There are tons of article out there explaining how to work with CORS, in our case we have to add some code to the start up class in our Todo API. Open Startup.cs and add the following two snippets.
#region Adding a CORS Policy services.AddCors(options => { options.AddPolicy("ToDoCors", builder => builder.AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin()); }); #endregion services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
#region Enabling CORS Policy app.UseCors("ToDoCors"); #endregion app.UseMvc();
Get ToDos
Todo.razor
@page "/todo" <!-- Import HTTP --> @inject HttpClient Http . . . @code { private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>(); string description; protected override async Task OnInitAsync() { todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos"); } private void AddTodo() { if (!string.IsNullOrWhiteSpace(description)) { todos.Add(new ToDo.Blazor.Models.ToDo { Description = description }); description = string.Empty; } } }
Todo.razor
Complete Todo.razor file (I added some styling to make this project more appealing), by default this project contains bootstrap 4 so I made use of it.
@page "/todo" <!-- Import HTTP --> @inject HttpClient Http <h1>Todo</h1> <div class="row"> <div class="col-10"> <input placeholder="Something todo" @bind="@description" class="form-control" /> </div> <div class="col-2"> <button @onclick="@AddTodo" class="btn btn-primary mb-2">Add Todo</button> </div> </div> <div class="card"> <div class="card-body"> <ul class="list-group"> @foreach (var todo in todos) { switch (@todo.Status) { case "Pending": <li class="list-group-item list-group-item-info d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <span class="font-weight-bold">@todo.Status</span> </li> break; case "In Progress": <li class="list-group-item list-group-item-success d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <span class="font-weight-bold">@todo.Status</span> </li> break; case "Done": <li class="list-group-item list-group-item-primary d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <span class="font-weight-bold">@todo.Status</span> </li> break; case "Cancelled": <li class="list-group-item list-group-item-warning d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <span class="font-weight-bold">@todo.Status</span> </li> break; } } </ul> </div> </div> @code { private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>(); string description; protected override async Task OnInitAsync() { todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos"); } private void AddTodo() { if (!string.IsNullOrWhiteSpace(description)) { todos.Add(new ToDo.Blazor.Models.ToDo { Description = description }); description = string.Empty; } } }
Adding a new Todo Item
First we will install a new package that will help up serialize our Todo object into JSON, so in you cmd stop your server using Ctrl C, then type the following command and hit enter.
dotnet add package Newtonsoft.Json --version 12.0.2
Add ToDo
To save our new Todos to the back end we need to modify our AddTodo method like below.
private async Task AddTodo() { if (!string.IsNullOrWhiteSpace(description)) { using (var client = new HttpClient()) { var newTodo = new ToDo.Blazor.Models.ToDo { Description = @description, Status = "Pending" }; var body = Newtonsoft.Json.JsonConvert.SerializeObject(newTodo); System.Net.Http.HttpResponseMessage result = await client.PostAsync(@"http://localhost:58635/api/ToDos", new StringContent(body, System.Text.Encoding.UTF8, "application/json")); string resultContent = await result.Content.ReadAsStringAsync(); if (result.IsSuccessStatusCode) { todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos"); } Console.WriteLine(resultContent); } description = string.Empty; } }
Update Todo
Finally we need to add the Modify Todo, so we have to make some changes to the Todo.razor page, please apply the following changes to your page and restart your app.
@page "/todo" <!-- Import HTTP --> @inject HttpClient Http <h1>Todo</h1> <div class="row"> <div class="col-8"> <input placeholder="Something todo" @bind="@description" class="form-control" /> </div> <div class="col-2"> <select @bind="@status"> <option value="Pending">Pending</option> <option value="In Progress">In Progress</option> <option value="Done">Done</option> <option value="Cancelled">Cancelled</option> </select> </div> <div class="col-2"> <button @onclick="@SaveTodo" class="btn btn-primary mb-2">Save Todo</button> </div> </div> <div class="card"> <div class="card-body"> <ul class="list-group"> @foreach (var todo in todos) { switch (@todo.Status) { case "Pending": <li class="list-group-item list-group-item-info d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <div> <span class="font-weight-bold">@todo.Status</span> <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span> </div> </li> break; case "In Progress": <li class="list-group-item list-group-item-success d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <div> <span class="font-weight-bold">@todo.Status</span> <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span> </div> </li> break; case "Done": <li class="list-group-item list-group-item-primary d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <div> <span class="font-weight-bold">@todo.Status</span> <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span> </div> </li> break; case "Cancelled": <li class="list-group-item list-group-item-warning d-flex justify-content-between align-items-center"> <span>@todo.Description</span> <div> <span class="font-weight-bold">@todo.Status</span> <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span> </div> </li> break; } } </ul> </div> </div> @code { private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>(); string description; string status; int _selectedId = 0; private void Edit(int id){ _selectedId = id; var selectedTodo= todos.FirstOrDefault(q => q.Id == id); description = selectedTodo.Description; status = selectedTodo.Status; Console.WriteLine(id); } protected override async Task OnInitAsync() { todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos"); } private async Task SaveTodo() { if (!string.IsNullOrWhiteSpace(description)) { using (var client = new HttpClient()) { var newTodo = new ToDo.Blazor.Models.ToDo { Id = _selectedId, Description = @description, Status = status }; var body = Newtonsoft.Json.JsonConvert.SerializeObject(newTodo); System.Net.Http.HttpResponseMessage result; if(_selectedId == 0){ result = await client.PostAsync(@"http://localhost:58635/api/ToDos", new StringContent(body, System.Text.Encoding.UTF8, "application/json")); }else{ result = await client.PutAsync($@"http://localhost:58635/api/ToDos/{_selectedId}", new StringContent(body, System.Text.Encoding.UTF8, "application/json")); } string resultContent = await result.Content.ReadAsStringAsync(); if (result.IsSuccessStatusCode) { todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos"); } Console.WriteLine(resultContent); } description = string.Empty; status = "Pending"; _selectedId = 0; } } }
We can not judge a framework with a simple app like this but at first glance I find that Blazor is the easiest framework I’ve worked with so far. Why ? I’m a c# developer there is no huge learning curve, you just have to understand how routing, events and binding work and that’s all.
Hope you’ve had some benefits from this simple tutorial, see you in upcoming posts.
Nice blog here! Also your website loads up fast! What hostare you using? Can I get your affiliate link to your host?I wish my site loaded up as fast as yours lol
Here is an affiliate link I hope you get a good discout.
outstanding article