ASP.NET Core MVC & Razor Pages Samples

Exploring asynchronous operations and API integration

Async Calls in ASP.NET Core MVC/Razor Pages

This sample demonstrates how to perform asynchronous operations, such as calling external APIs, within your ASP.NET Core MVC or Razor Pages applications. Asynchronous programming is crucial for building responsive and scalable web applications, especially when dealing with I/O-bound operations like network requests.

Why Use Async?

By using asynchronous methods (marked with the `async` and `await` keywords), your application can free up threads while waiting for long-running operations to complete. This allows the web server to handle other incoming requests, significantly improving performance and user experience, preventing thread pool starvation, and reducing resource consumption.

Example Scenario: Fetching Data from a Public API

We'll simulate fetching data from a hypothetical public API. This could be a service that provides weather information, stock prices, or user profiles. For this example, we'll use a simple placeholder API call.

Controller/Page Model Implementation (C#)

Here's how you might implement an asynchronous action method in an MVC controller or an asynchronous handler in a Razor Pages page model.

MVC Controller Example


using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json; // Or System.Text.Json

namespace MyWebApp.Controllers
{
    public class DataController : Controller
    {
        private readonly HttpClient _httpClient;

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

        public async Task<IActionResult> Index()
        {
            var apiUrl = "https://api.example.com/data/items";
            try
            {
                // Asynchronously call the external API
                var response = await _httpClient.GetAsync(apiUrl);

                if (response.IsSuccessStatusCode)
                {
                    var jsonContent = await response.Content.ReadAsStringAsync();
                    // Deserialize JSON response
                    var data = JsonConvert.DeserializeObject<List<string>>(jsonContent);

                    // Pass data to the view
                    return View(data);
                }
                else
                {
                    // Handle API error
                    return StatusCode((int)response.StatusCode, "Error fetching data from API.");
                }
            }
            catch (HttpRequestException ex)
            {
                // Handle network or request errors
                return StatusCode(500, $"API request failed: {ex.Message}");
            }
        }
    }
}
            

Razor Pages Page Model Example


using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Newtonsoft.Json; // Or System.Text.Json

namespace MyWebApp.Pages
{
    public class DataModel : PageModel
    {
        private readonly HttpClient _httpClient;
        public List<string> MyData { get; set; }

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

        public async Task OnGetAsync()
        {
            var apiUrl = "https://api.example.com/data/items";
            try
            {
                // Asynchronously call the external API
                var response = await _httpClient.GetAsync(apiUrl);

                if (response.IsSuccessStatusCode)
                {
                    var jsonContent = await response.Content.ReadAsStringAsync();
                    // Deserialize JSON response
                    MyData = JsonConvert.DeserializeObject<List<string>>(jsonContent);
                }
                else
                {
                    // Handle API error
                    ModelState.AddModelError("", "Error fetching data from API.");
                }
            }
            catch (HttpRequestException ex)
            {
                // Handle network or request errors
                ModelState.AddModelError("", $"API request failed: {ex.Message}");
            }
        }
    }
}
            

Setting up HttpClient

It's recommended to register HttpClient as a singleton service in your application's Startup.cs or Program.cs file:


// In Startup.cs (or Program.cs for .NET 6+)
public void ConfigureServices(IServiceCollection services)
{
    // ... other services

    services.AddHttpClient(); // Registers HttpClient as a transient service

    // Or configure with a base address and default headers
    services.AddHttpClient("MyApiClient", client =>
    {
        client.BaseAddress = new Uri("https://api.example.com/");
        client.DefaultRequestHeaders.Add("Accept", "application/json");
    });
}
            

Razor View /cshtml Example

Displaying the fetched data in a view:


@model MyWebApp.Pages.DataModel // For Razor Pages
// Or @model IEnumerable<string> for MVC views

// For MVC Controller: View
// @{ ViewData["Title"] = "API Data"; }

// For Razor Pages: _ViewImports.cshtml might handle this
@{
    ViewData["Title"] = "API Data";
}

<h2>Data from External API</h2>

<p>
    This data was fetched asynchronously from an external service.
</p>

<h3>Items:</h3>

@if (Model.MyData != null && Model.MyData.Any())
{
    <ul>
        @foreach (var item in Model.MyData)
        {
            <li>@item</li>
        }
    </ul>
}
else
{
    <p>No data available or an error occurred.</p>
}

// For MVC Controller: View
// @{
//     var data = Model as IEnumerable<string>;
// }
// <h2>Data from External API</h2>
// <p>This data was fetched asynchronously from an external service.</p>
// <h3>Items:</h3>
// @if (data != null && data.Any())
// {
//     <ul>
//         @foreach (var item in data)
//         {
//             <li>@item</li>
//         }
//     </ul>
// }
// else
// {
//     <p>No data available or an error occurred.</p>
// }
            

Advanced Considerations

// Then use: hljs.highlightAll();