Author : HASSAN MD TAREQ | Updated : 2020/10/21

Introduction

Setup for dependency injection

  • Set BaseUri in appsettings.json
    • During development, use dev BaseUri (i.e. https://localhost:44321/api/)
    • For production, set BaseUri in Azure App Service or set by Azure DevOps pipeline (variable translation)
  • Configure HTTP Client in Startup.cs
    • Scope: Singleton
    • Get ServicePoint and set desired properties (i.e. servicePoint.ConnectionLeaseTimeout = 6000)

appsettings.json

{
  "APIHost": "https://localhost:44321/api/",
}

Constants.cs

public static class Constants
{
	public static readonly string KEY_API_BASE_URI = "APIHost";
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
	// ... ... ...

	// Configure HTTP Client
	var apiAddress = Configuration[Constants.KEY_API_BASE_URI];
	Uri apiBaseUri = new Uri(apiAddress);

	HttpClient apiClient = new HttpClient();
	apiClient.BaseAddress = apiBaseUri;
	var servicePoint = ServicePointManager.FindServicePoint(apiBaseUri);
	servicePoint.ConnectionLeaseTimeout = 60000;

	services.AddSingleton<HttpClient>(apiClient);
}

Usage in controller by dependency injection

ProductController .cs

using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace Foo.Controllers
{
    public class ProductController : Controller
    {
        HttpClient client;

        public ProductController(HttpClient apiClient)
        {
            client = apiClient;
        }

        public async Task<IActionResult> Index()
        {
            var response = await client.GetStringAsync("product");
            var products = JArray.Parse(response);
            return View(products);
        }

        public async Task<IActionResult> Detail(int id)
        {
            var response = await client.GetStringAsync($"product/{id}" );

            var product = JsonConvert.DeserializeObject<Models.Product>(response);
			
            return View(product);
        }
    }
}

public class Product
{
	public int ID { get; set; }
	public string Name { get; set; }
	public string Description { get; set; }

	public string ImageTitle { get; set; }
	public string Image { get; set; }
}

Json Serialization

To serialize

using System.Text.Json;
using System.Text.Json.Serialization;

var jsonStr = JsonSerializer.Serialize(MyObject)

To deserialize

var weatherForecast = JsonSerializer.Deserialize<MyObject>(jsonStr);

Using vanilla IHttpClientFactory and HttpClient

Typed client

RepoService.cs

public class RepoService
{
    private readonly HttpClient _httpClient;

    public RepoService(HttpClient client)
    {
        _httpClient = client;
    }

    public async Task<IEnumerable<string>> GetRepos()
    {
        var response = await _httpClient.GetAsync("aspnet/repos");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync(); // inline 'using' => C# 8.0+
        return await JsonSerializer.DeserializeAsync<IEnumerable<string>>(responseStream);
    }
}

Startup.ConfigureServices(...)

services.AddHttpClient<RepoService>(repoClient =>
{
    repoClient.BaseAddress = new Uri("https://api.github.com/");
    repoClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    repoClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

Usage

public class TypedClientModel : PageModel
{
    private readonly GitHubService _gitHubService;

    public TypedClientModel(GitHubService gitHubService)
    {
        _gitHubService = gitHubService;
    }

    public async Task OnGet()
    {
        try
        {
            var issues = await _gitHubService.GetAspNetDocsIssues();
        }
        catch(HttpRequestException)
        {
            // ... ... ...
        }
    }
}

See:

Querying CosmosDB

Flurl

Refit

Generating http client with NSwag