Sat. Aug 9th, 2025

ASP.NET Core web app that consumes an API

Web applications often need to interact with external services to retrieve or exchange data. The most common approach is to create web applications that consume APIs. In this post I will cover one example of communication between web application and API service.

Introduction

The following tutorial demonstrates how to build an ASP.NET Core web application that consumes a RESTful API. This topic is related to the applied skill assessment which validates practical experience in developing and integrating web services. I will demonstrate in this post what you need to know to pass the skill assessment.

Github project repository

Link to project github repository: Ticket management system

Repository consists of two projects:

  • TicketsAPI – REST API service
  • TicketsWebApp – Web application that consumes API service

Understanding the API Service

Before we go into implementation details of the web application we first need to check the API specifications. The API serves as the backend for our ticket management system, providing endpoints to manage both providers and tickets.

Here’s what you need to know about the API:

  • Endpoints: The API exposes multiple endpoints to handle CRUD operations for both providers and tickets. You will find endpoints to create, read, update, and delete resources.
  • Swagger Documentation: API service has swagger enabled. Before we develop a web app which will consume API, we need to get familiar with the structure of the endpoints by opening the Swagger UI at http://localhost:5001/swagger. In the Swagger UI, you’ll see a list of all available endpoints. Spend some time here to understand:
    • Which endpoints are available
    • What types of HTTP requests (GET, POST, PUT, DELETE) are supported by each endpoint
    • What parameters are required for each request
    • The structure of the responses you will receive
  • API Authentication: If you take a look at the swagger, you can also see that all the endpoints require authentication. The API is secured using an API key which you must include in the request headers. If the API key is not provided in request headers of the web application, you will receive 401

Implementing the ASP.NET Core Web Application

Once you’re familiar with the API, the next step is to develop the ASP.NET Core web application.

Web application structure

Setting Up API Communication

Since REST API service is using api key as authentication method, we will also need to include in our request header the api key or else we will get 401 unauthorized response:

builder.Services.AddHttpClient<IApiService, ApiService>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["ApiSettings:BaseUrl"]);
    client.DefaultRequestHeaders.Add("Accept", "application/json");
    client.DefaultRequestHeaders.Add("X-API-Key", builder.Configuration["ApiSettings:ApiKey"]);
});

Business logic implementation

Next comes the implementation which we can do in multiple ways. I decided to implement all the services business logic into ApiService class.

Interface IApiService

public interface IApiService
{
    Task<IEnumerable<Ticket>> GetAllTicketsAsync();
    Task<EditTicketResponse> GetTicketByIdAsync(int id);
    Task<bool> CreateTicketAsync(CreateTicketRequest ticket);
    Task<bool> UpdateTicketAsync(int id, UpdateTicketRequest ticket);
    Task<bool> DeleteTicketAsync(int id);
    Task<IEnumerable<Provider>> GetAllProvidersAsync();
    Task<Provider> GetProviderByIdAsync(int id);
    Task<ProviderDetails> GetTicketsByProviderIdAsync(int providerId);
}

Service class ApiService implementation

public class ApiService : IApiService
{
    private readonly HttpClient _httpClient;

    public ApiService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<IEnumerable<Ticket>> GetAllTicketsAsync()
    {
        var response = await _httpClient.GetAsync("/tickets");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<IEnumerable<Ticket>>();
    }

    public async Task<EditTicketResponse> GetTicketByIdAsync(int id)
    {
        var response = await _httpClient.GetAsync($"/tickets/{id}");
        return await response.Content.ReadFromJsonAsync<EditTicketResponse>();
    }

    public async Task<bool> CreateTicketAsync(CreateTicketRequest ticket)
    {
        var response = await _httpClient.PostAsJsonAsync("/tickets", ticket);
        return response.IsSuccessStatusCode;
    }

    public async Task<bool> UpdateTicketAsync(int id, UpdateTicketRequest ticket)
    {
        var response = await _httpClient.PutAsJsonAsync($"/tickets/{id}", ticket);
        return response.IsSuccessStatusCode;
    }

    public async Task<bool> DeleteTicketAsync(int id)
    {
        var response = await _httpClient.DeleteAsync($"/tickets/{id}");
        return response.IsSuccessStatusCode;
    }

    public async Task<IEnumerable<Provider>> GetAllProvidersAsync()
    {
        var response = await _httpClient.GetAsync("/providers");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<IEnumerable<Provider>>();
    }

    public async Task<Provider> GetProviderByIdAsync(int id)
    {
        var response = await _httpClient.GetAsync($"/provider/{id}");
        return await response.Content.ReadFromJsonAsync<Provider>();
    }

    public async Task<ProviderDetails> GetTicketsByProviderIdAsync(int providerId)
    {
        var response = await _httpClient.GetAsync($"/tickets/provider/{providerId}");
        return await response.Content.ReadFromJsonAsync<ProviderDetails>();
    }
}

Models

In a models part we have classes which are basically being mapped to the pages in web application. We have three main pages for displaying data from API service:

  • Tickets lists the information about the tickets and provider which have created them
  • Providers page lists the list of the providers that are registered at the moment in the REST API database
  • ProviderDetails lists the tickets that are created by the provider

Request / Response

Web application uses two custom requests to comply with the API service request format:

  • For creation of the new ticket
  • For the update of the existing ticket

There are also two custom responses which are used for:

  • To get ticket by Id
  • To create custom list of tickets for ProviderDetails page

Pages / PageModels

By using dependency injection the ApiService is used in all the PageModels of the razor pages. Example of the usage of the PageModel + ApiService:

public class CreateTicketModel : PageModel
{
    private readonly IApiService _apiService;

    public CreateTicketModel(IApiService apiService)
    {
        _apiService = apiService;
    }

    [BindProperty]
    public CreateTicketRequest TicketRequest { get; set; }

    public IEnumerable<Provider> Providers { get; set; }

    public async Task OnGetAsync()
    {
        Providers = await _apiService.GetAllProvidersAsync();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            Providers = await _apiService.GetAllProvidersAsync();
            return Page();
        }

        var success = await _apiService.CreateTicketAsync(TicketRequest);
        if (success)
        {
            return RedirectToPage("Tickets");
        }

        ModelState.AddModelError(string.Empty, "Failed to create ticket.");
        Providers = await _apiService.GetAllProvidersAsync();
        return Page();
    }
}

You can go over official Microsoft learning path here: Develop an ASP.NET Core web app that consumes an API

Clone repositories and play around with the apps:


By Denis