How to consume REST API in Blazor Application

In this blog post, we will discuss how to consume REST API in Blazor Server Application using the .NET Frameworks HttpClient class, and will cover how to invoke GET, POST, PUT and DELETE APIs and display the method responses using an HTML table.

Blazor is a new framework provided by Microsoft to build interactive client-side web applications using C# programming language and Razor syntax on top of the .NET Core framework. The beauty of this framework is that the developer need not know any JavaScript and can leverage the existing .NET, .NET Core frameworks and its associated NuGet packages to power the Blazor application. The developer can build a Blazor application that can run on the server or directly inside the client browsers.

REST Server and API details

We are going to use an existing freely available REST server resreq.in, this server provides REST APIs that can be used for testing. We are going to invoke the following GET, POST, PUT and DELETE APIs from our code. The details and the response are as follows:

GET (https://reqres.in/api/users/)

The GET API returns the list of users and the same will be displayed in the HTML table. The response of the API is given below:

{
   "page":1,
   "per_page":6,
   "total":12,
   "total_pages":2,
   "data":[
      {
         "id":1,
         "email":"george.bluth@reqres.in",
         "first_name":"George",
         "last_name":"Bluth",
         "avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
      },
    ...........   
      {
         "id":6,
         "email":"tracey.ramos@reqres.in",
         "first_name":"Tracey",
         "last_name":"Ramos",
         "avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/bigmancho/128.jpg"
      }
   ],
   "ad":{
      "company":"StatusCode Weekly",
      "url":"http://statuscode.org/",
      "text":"A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things."
   }
}

POST (https://reqres.in/api/users/)

The POST API will be used to create a new user on the server, on a successful operation, the server provides a user id and created a timestamp back as a response. The sample request and response is given below:

A sample request:

{
    "name": "john doe",
    "job": "software engineer"
}

Sample response from the server

{
    "name": "john doe",
    "job": "software engineer",
    "id": "961",
    "createdAt": "2020-04-21T18:37:49.030Z"
}

PUT (https://reqres.in/api/users/{userid})

The PUT API is used to update an existing user on the server, on successful update the server provides the updated timestamp back as a response. The sample request and response for the operation are given below:

A sample PUT request body:

{
    "name": "john doe",
    "job": "programmer"
}

Sample response from the server

{
    "name": "john doe",
    "job": "programmer",
    "updatedAt": "2020-04-21T18:44:34.766Z"
}

DELETE (https://reqres.in/api/users/{userid})

This API is used to delete an existing resource on the server. The user-id of the resource that needs to be deleted is passed in the API path and on successful deletion, the server response back with HTTP 204 code.

Calling REST API in Blazor Server Application

Now let us consume these REST APIs in Blazor Application, we will do this in a Blazor Server Application (at the time of writing Blazor WebAssembly is in preview 2). The step to create a Blazor Server Application is shown in this quick 20 seconds video.

How to create a Blazor Server application in Visual Studio 2019

Register System.Net.Http.HTTPClient in ConfigureServices() method in Startup.cs.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();
            services.AddScoped<HttpClient>(s =>
            {
                return new HttpClient { BaseAddress = new Uri(@"https://reqres.in/") };
            });
        }

Add the following three classes in the Data directory of the project. These classes will be used for sending the request and getting responses while invoking the method calls. Also, import the Newtonsoft.Json namespace in each of these classes.

User.cs: This class represent the user data the server returns.

 public class User
    {
        [JsonProperty("id")]
        public int Id { get; set; }
        
        [JsonProperty("email")]
        public string Email { get; set; }

        [JsonProperty("first_name")]
        public string FirstName { get; set; }

        [JsonProperty("last_name")]
        public string LastName { get; set; }

        [JsonProperty("avatar")]
        public string AvatarURI { get; set; }
    }

Response.cs: This class represent the response return by the server for calling the GET API, the Users property will be used to display the list of User on the user interface.

public class Response
{
    [JsonProperty("page")]
    public int Page { get; set; }

    [JsonProperty("per_page")]
    public int PerPage { get; set; }

    [JsonProperty("total")]
    public int Total { get; set; }

    [JsonProperty("total_pages")]
    public int TotalPages { get; set; }

    [JsonProperty("data")]
    public List<User> Users { get; set; }
}

CRUDUser.cs : This class is used for sending request body and receiving response from the server for POST, PUT and DELETE APIs operations.

public class CRUDUser
{
    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("job")]
    public string Job { get; set; }

    [JsonProperty("createdAt")]
    public DateTime CreatedTimestamp { get; set; }

    [JsonProperty("updatedAt")]
    public DateTime UpdatedTimestamp { get; set; }
}
  

Modify the code of index.razor file with following code: The class required to inject the HttpClient instance, and we need to add using statements for Newtonsoft.Json and Data.

@page "/"

@inject HttpClient client

@using Newtonsoft.Json
@using Data


<div class="container-fluid">

    <div class="row justify-content-center" style="margin-bottom:50px">
        <h3 class="text-center">Calling REST-APIs</h3>
    </div>

    <div class="row" style="margin:25px;">
        <strong>Get Operation </strong><hr />
        @if (Users != null)
        {
            <table class="table table-bordered table-striped">
                <thead class="thead-dark">
                    <tr>
                        <th>Id</th>
                        <th>First Name</th>
                        <th>Last Name</th>
                        <th>Email</th>
                        <th>Avatar</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var user in Users)
                    {
                        <tr class="justify-content-center">
                            <td>@user.Id</td>
                            <td>@user.FirstName</td>
                            <td>@user.LastName</td>
                            <td>@user.Email</td>
                            <td><img src="@user.AvatarURI" height="30" width="30" /></td>
                        </tr>
                    }
                </tbody>
            </table>
        }
        @if (!Users.Any())
        {
            <div class="justify-content-center">
                <h5>No records to show</h5>
            </div>
        }
    </div>

    <div class="row" style="margin:10px;margin-bottom:50px;">
        <div class="col"><button class="btn btn-info" @onclick="GetUser">Get Users</button></div>
    </div>

    <div class="row" style="margin:25px; margin-top:100px;">

        <strong>Post, Put and Delete Operations Table Data</strong><hr />

        @if (CRUDUsers != null)
        {
            <table class="table table-bordered table-striped">
                <thead class="thead-dark">
                    <tr>
                        <th>Id</th>
                        <th>Full Name</th>
                        <th>Job</th>
                        <th>Created Timestamp</th>
                        <th>Updated Timestamp</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var user in CRUDUsers)
                    {
                        <tr>
                            <td>@user.Id</td>
                            <td>@user.Name</td>
                            <td>@user.Job</td>
                            <td>@user.CreatedTimestamp</td>
                            <td>@user.UpdatedTimestamp</td>
                        </tr>
                    }
                </tbody>
            </table>
        }
        @if (!CRUDUsers.Any())
        {
            <div class="justify-content-center">
                <h5>No records to show</h5>
            </div>
        }
    </div>

    <div class="row" style="margin:10px; margin-top:70px;">
        <div class="col"><button class="btn btn-success" @onclick="ShowHideCreateUserForm">Show/Hide Create Form</button>   </div>
        <div class="col"><button class="btn btn-warning" @onclick="ShowHideUpdateUserForm">Show/Hide Update User Form</button></div>
        <div class="col"><button class="btn btn-danger" @onclick="ShowHideDeleteUserForm">Show/Hide Delete User Form</button></div>
    </div>

    <div class="row" hidden="@ShowCreateUserForm" style="border:3px solid black;margin:20px;">
        <EditForm Model="@crudUser">
            <div class="row form-inline" style="margin:20px;">
                <div class="form-group" style="margin:20px;">
                    <label for="Name" style="margin:10px;">Name</label>
                    <InputText Id="Name" @bind-Value="@crudUser.Name"></InputText>
                </div>
                <div class="form-group" style="margin:20px;">
                    <label for="Job" style="margin:10px;">Job Description</label>
                    <InputText Id="Job" @bind-Value="@crudUser.Job"></InputText>
                </div>

                <button class="btn btn-success" @onclick="@CreateNewUser">Create</button>
            </div>
        </EditForm>
    </div>

    <div class="row" hidden="@ShowUpdateUserForm" style="border:3px solid black;margin:20px;">
        <EditForm Model="@selectedUser">
            <div class="row form-inline" style="margin:20px;">
                <div class="form-group" style="margin:20px;">
                    <label for="UseId" style="margin:10px;">User Id</label>
                    <select id="UserId" @onchange=@UpdateSelectedUser>
                        @if (CRUDUsers != null)
                        {
                            @foreach (var user in CRUDUsers)
                            {
                                <option value="@user.Id">@user.Id</option>
                            }
                        }
                    </select>
                </div>

                <div class="form-group" style="margin:20px;">
                    <label for="Name" style="margin:10px;">Name</label>
                    <InputText Id="Name" @bind-Value="@selectedUser.Name"></InputText>
                </div>
                <div class="form-group" style="margin:20px;">
                    <label for="Job" style="margin:10px;">Job Description</label>
                    <InputText Id="Job" @bind-Value="@selectedUser.Job"></InputText>
                </div>

                <button class="btn btn-warning" @onclick="@UpdateUser">Update</button>
            </div>
        </EditForm>
    </div>

    <div class="row" hidden="@ShowUpdateDeleteForm" style="border:3px solid black;margin:20px;">
        <EditForm Model="@selectedUser">
            <div class="row form-inline" style="margin:20px;">
                <div class="form-group" style="margin:20px;">
                    <label for="UseId" style="margin:10px;">User Id</label>
                    <select id="UserId" @onchange=@UpdateSelectedUser>
                        @if (CRUDUsers != null)
                        {
                            @foreach (var user in CRUDUsers)
                            {
                                <option value="@user.Id">@user.Id</option>
                            }
                        }
                    </select>

                    <div class="form-group" style="margin:20px;">
                        <label for="Name" style="margin:10px;">Name</label>
                        <InputText Id="Name" @bind-Value="@selectedUser.Name"></InputText>
                    </div>
                    <div class="form-group" style="margin:20px;">
                        <label for="Job" style="margin:10px;">Job Description</label>
                        <InputText Id="Job" @bind-Value="@selectedUser.Job"></InputText>
                    </div>

                </div>
                <button class="btn btn-danger" @onclick="@DeleteUser">Delete</button>
            </div>
        </EditForm>

    </div>

</div>

@code {

    List<User> Users = new List<User>();

    private async void GetUser()
    {
        var apiName = "api/users";
        var httpResponse = await client.GetAsync(apiName);

        if (httpResponse.IsSuccessStatusCode)
        {
            Response responseData = JsonConvert.DeserializeObject<Response>(await httpResponse.Content.ReadAsStringAsync());
            Users = responseData.Users;
            StateHasChanged();
        }
    }

    List<CRUDUser> CRUDUsers = new List<CRUDUser>();

    CRUDUser crudUser = new CRUDUser();

    bool ShowCreateUserForm = true;

    private void ShowHideCreateUserForm()
    {
        ShowCreateUserForm = !ShowCreateUserForm;
    }

    private async Task CreateNewUser()
    {
        string apiName = "api/users";
        var postData = JsonConvert.SerializeObject(crudUser);
        var response = await client.PostAsync(apiName, new StringContent(postData));

        if (response.IsSuccessStatusCode)
        {
            var content = await response.Content.ReadAsStringAsync();
            CRUDUser user = JsonConvert.DeserializeObject<CRUDUser>(await response.Content.ReadAsStringAsync());
            if (user != null)
            {
                user.Name = crudUser.Name;
                user.Job = crudUser.Job;
                CRUDUsers.Add(user);
                crudUser = new CRUDUser();
            }
        }
    }

    CRUDUser selectedUser = new CRUDUser();
    bool ShowUpdateUserForm = true;

    private void ShowHideUpdateUserForm()
    {
        ShowUpdateUserForm = !ShowUpdateUserForm;
    }

    void UpdateSelectedUser(ChangeEventArgs e)
    {
        int id = int.Parse(e.Value.ToString());
        selectedUser = CRUDUsers.FirstOrDefault(u => u.Id == id);
    }

    private async Task UpdateUser()
    {
        string apiName = string.Format($"api/users/{selectedUser.Id}");

        var postData = JsonConvert.SerializeObject(selectedUser);
        var response = await client.PutAsync(apiName, new StringContent(postData));

        if (response.IsSuccessStatusCode)
        {
            var content = await response.Content.ReadAsStringAsync();
            CRUDUser user = JsonConvert.DeserializeObject<CRUDUser>(await response.Content.ReadAsStringAsync());
            if (user != null)
            {
                user.Id = selectedUser.Id;
                user.Name = selectedUser.Name;
                user.Job = selectedUser.Job;
                user.CreatedTimestamp = selectedUser.CreatedTimestamp;

                // Remove the old item from the list and replace it with the updated one
                var temp = CRUDUsers.FirstOrDefault(u => u.Id == selectedUser.Id);
                CRUDUsers.Remove(temp);
                CRUDUsers.Add(user);
            }
        }
    }

    bool ShowUpdateDeleteForm = true;
    private void ShowHideDeleteUserForm()
    {
        ShowUpdateDeleteForm = !ShowUpdateDeleteForm;
    }

    private async Task DeleteUser()
    {
        string apiName = string.Format($"api/users/{selectedUser.Id}");
        var response = await client.DeleteAsync(apiName);

        if (response.IsSuccessStatusCode)
        {
            var temp = CRUDUsers.FirstOrDefault(u => u.Id == selectedUser.Id);
            CRUDUsers.Remove(temp);
            selectedUser = new CRUDUser();
        } 
    } 
}

On executing the application the following User Interface will be displayed using which the user can invoke all the mentioned GET, POST, PUT and DELETE APIs calls.

consume REST API in Blazor Application
UI to consume REST API in Blazor application

The output can be checked in the following video.

REST API in Blazor – Application output

The HttpClient class helps us to consume REST API in Blazor application with ease, the class provides all the methods to call the respective REST APIs.

Source Code

https://github.com/technicalbundle/blazorservercrudwithrestapi

This concludes the post on how to consume REST API in Blazor application, I hope you find this post helpful. Thanks for visiting, Cheers!!!.

[Further Readings: Blazor Lifecycle Methods |  A Simple way to Call Javascript in Blazor Application |  Creational Design Patterns |  Builder Design Pattern in C# |  Prototype Design Pattern in C# |  Top 5 Blazor Component Libraries |  Abstract Factory Design Pattern in C# |  Factory Method Pattern in C# |  Singleton Design Pattern in C# |  Introduction to Design Patterns |  Microsoft C# Version History |  Microsoft .NET Core Versions History |  Microsoft .NET Framework Version History |  Introduction to WPF in .NET Core |  Useful Visual Studio 2019 extensions for database projects |  Machine Learning Model Generation  ]  

4.8 4 votes
Article Rating
Subscribe
Notify of
guest
12 Comments
oldest
newest most voted
Inline Feedbacks
View all comments
12
0
Would love your thoughts, please comment.x
()
x