Bagikan melalui


IHttpClientFactory dengan .NET

Dalam artikel ini, Anda akan mempelajari cara menggunakan IHttpClientFactory antarmuka untuk membuat HttpClient jenis dengan berbagai dasar -dasar .NET, seperti injeksi dependensi (DI), pengelogan, dan konfigurasi. Jenis HttpClient ini diperkenalkan pada .NET Framework 4.5, yang dirilis pada tahun 2012. Dengan kata lain, tipe ini sudah ada sejak beberapa waktu yang lalu. HttpClient digunakan untuk membuat permintaan HTTP dan menangani respons HTTP dari sumber daya web yang diidentifikasi oleh Uri. Protokol HTTP merupakan sebagian besar dari seluruh lalu lintas internet.

Dengan prinsip pengembangan aplikasi modern yang mendorong praktik terbaik, IHttpClientFactory berfungsi sebagai abstraksi pabrik yang bisa membuat instans HttpClient dengan konfigurasi kustom. IHttpClientFactory diperkenalkan dalam .NET Core 2.1. Beban kerja .NET berbasis HTTP umum bisa memanfaatkan middleware pihak ketiga yang tangguh dan menangani kesalahan sementara dengan mudah.

Catatan

Jika aplikasi Anda memerlukan cookie, mungkin lebih baik untuk menghindari penggunaan IHttpClientFactory di aplikasi Anda. Untuk cara alternatif mengelola klien, lihat Panduan menggunakan klien HTTP.

Penting

Manajemen HttpClient instans seumur hidup yang dibuat sama sekali berbeda dari IHttpClientFactory instans yang dibuat secara manual. Strateginya adalah menggunakan klien berumur pendek yang dibuat oleh IHttpClientFactory atau klien berumur panjang dengan PooledConnectionLifetime penyiapan. Untuk informasi selengkapnya, lihat bagian manajemen seumur hidup HttpClient dan Panduan untuk menggunakan klien HTTP.

Jenis IHttpClientFactory

Semua kode sumber sampel pada artikel ini bergantung pada paket NuGet Microsoft.Extensions.Http. Selain itu, permintaan HTTP GET dibuat ke {JSON} Placeholder API gratis untuk mendapatkan objek penggunaTodo.

Saat Anda memanggil salah satu metode ekstensi AddHttpClient, Anda menambahkan IHttpClientFactory dan layanan terkait ke IServiceCollection. Tipe IHttpClientFactory menawarkan manfaat berikut ini:

  • Mengekspos kelas HttpClient sebagai jenis yang siap dengan DI.
  • Menyediakan lokasi pusat untuk penamaan dan konfigurasi instans HttpClient logis.
  • Mengkodifikasi konsep middleware keluar via pendelegasian handler di HttpClient.
  • Jenis ini juga menyediakan ekstensi untuk middleware berbasis Polly untuk memanfaatkan pendelegasian handler di HttpClient.
  • Mengelola penembolokan dan masa pakai instans yang mendasarinya HttpClientHandler . Manajemen otomatis menghindari masalah Domain Name System (DNS) yang terjadi saat mengelola masa pakai HttpClient secara manual.
  • Menambahkan pengalaman pengelogan yang bisa dikonfigurasi (melalui ILogger) untuk semua permintaan yang dikirim melalui klien yang dibuat oleh pabrik.

Pola konsumsi

Ada beberapa cara menggunakan IHttpClientFactory dalam aplikasi:

Pendekatan terbaiknya tergantung pada persyaratan aplikasi.

Penggunaan dasar

Untuk mendaftarkan IHttpClientFactory, panggil AddHttpClient:

using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();

using IHost host = builder.Build();

Menggunakan layanan dapat membutuhkan IHttpClientFactory sebagai parameter konstruktor dengan DI. Kode berikut ini menggunakan IHttpClientFactory untuk membuat instansHttpClient:

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;

namespace BasicHttp.Example;

public sealed class TodoService(
    IHttpClientFactory httpClientFactory,
    ILogger<TodoService> logger)
{
    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        // Create the client
        using HttpClient client = httpClientFactory.CreateClient();
        
        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo types
            Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
                $"https://jsonplaceholder.typicode.com/todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

Menggunakan IHttpClientFactory seperti pada contoh sebelumnya adalah cara yang baik untuk merefaktor aplikasi yang ada. Ini tidak berdampak pada bagaimana HttpClient digunakan. Di tempat-tempat di mana instans HttpClient dibuat di aplikasi yang ada, ganti kemunculan tersebut dengan panggilan ke CreateClient.

Klien bernama

Klien bernama adalah pilihan yang baik saat:

  • Aplikasi membutuhkan banyak kegunaan HttpClient yang berbeda.
  • Banyak HttpClient instans memiliki konfigurasi yang berbeda.

Konfigurasi untuk nama HttpClient dapat ditentukan selama pendaftaran pada IServiceCollection:

using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);

builder.Services.AddHttpClient(
    httpClientName,
    client =>
    {
        // Set the base address of the named client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

Pada kode sebelumnya, klien dikonfigurasi dengan:

  • Nama yang ditarik dari konfigurasi di bawah "TodoHttpClientName".
  • Alamat dasar https://jsonplaceholder.typicode.com/.
  • Header "User-Agent".

Anda bisa menggunakan konfigurasi untuk menentukan nama klien HTTP, yang sangat membantu untuk menghindari kesalahan nama klien saat menambahkan dan membuat. Pada contoh ini, file appsettings.json digunakan untuk mengonfigurasi nama klien HTTP:

{
    "TodoHttpClientName": "JsonPlaceholderApi"
}

Sangat mudah untuk memperluas konfigurasi ini dan menyimpan lebih banyak detail tentang bagaimana Anda ingin memfungsikan klien HTTP Anda. Untuk informasi selengkapnya, lihat Konfigurasi di .NET.

Membuat klien

Setiap kali CreateClient disebut:

  • Instans HttpClient baru dibuat.
  • Tindakan konfigurasi dipanggil.

Untuk membuat klien bernama, berikan namanya ke dalam CreateClient:

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;

namespace NamedHttp.Example;

public sealed class TodoService
{
    private readonly IHttpClientFactory _httpClientFactory = null!;
    private readonly IConfiguration _configuration = null!;
    private readonly ILogger<TodoService> _logger = null!;

    public TodoService(
        IHttpClientFactory httpClientFactory,
        IConfiguration configuration,
        ILogger<TodoService> logger) =>
        (_httpClientFactory, _configuration, _logger) =
            (httpClientFactory, configuration, logger);

    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        // Create the client
        string? httpClientName = _configuration["TodoHttpClientName"];
        using HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");

        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo type
            Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
                $"todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

Pada kode sebelumnya, permintaan HTTP tidak perlu menentukan nama host. Kode hanya bisa melewati jalur karena alamat dasar yang dikonfigurasi untuk klien digunakan.

Klien yang berjenis

Klien yang berjenis:

  • Memberikan kemampuan yang sama seperti klien bernama tanpa perlu menggunakan string sebagai kunci.
  • Memberikan bantuan IntelliSense dan pengkompilasi saat menggunakan klien.
  • Menyediakan satu lokasi untuk mengonfigurasi dan berinteraksi dengan HttpClient tertentu. Misalnya, klien bertitik tunggal dapat digunakan:
    • Untuk satu titik akhir backend.
    • Untuk merangkum semua logika yang berhubungan dengan titik akhir.
  • Bekerja dengan DI dan bisa disuntikkan jika diperlukan di aplikasi.

Klien yang berjenis menerima parameter HttpClient dalam konstruktornya:

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;

namespace TypedHttp.Example;

public sealed class TodoService(
    HttpClient httpClient,
    ILogger<TodoService> logger) : IDisposable
{
    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo type
            Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
                $"todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }

    public void Dispose() => httpClient?.Dispose();
}

Dalam kode sebelumnya:

  • Konfigurasi diatur ketika klien berjenis ditambahkan ke koleksi layanan.
  • HttpClient ditetapkan sebagai variabel cakupan kelas (bidang), dan digunakan dengan API yang diekspos.

Metode khusus API bisa dibuat yang mengekspos fungsionalitas HttpClient. Misalnya, metode ini GetUserTodosAsync merangkum kode untuk mengambil objek khusus Todo pengguna.

Kode berikut memanggil AddHttpClient untuk mendaftarkan kelas klien yang ditik:

using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient<TodoService>(
    client =>
    {
        // Set the base address of the typed client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

Klien berjenis terdaftar sebagai sementara dengan DI. Dalam kode sebelumnya, AddHttpClient mendaftarkan TodoService sebagai layanan sementara. Pendaftaran ini menggunakan metode pabrik untuk:

  1. Buat instans HttpClient.
  2. Membuat instans TodoService, meneruskan instans HttpClient ke konstruktornya.

Penting

Menggunakan klien yang dititik dalam layanan singleton bisa berbahaya. Untuk informasi selengkapnya, lihat bagian Hindari klien Yang Ditik di layanan singleton.

Catatan

Saat mendaftarkan klien yang ditik dengan AddHttpClient<TClient> metode , TClient jenis harus memiliki konstruktor yang menerima HttpClient parameter. Selain itu, TClient jenis tidak boleh didaftarkan dengan kontainer DI secara terpisah.

Klien yang dihasilkan

IHttpClientFactory bisa digunakan dalam kombinasi dengan pustaka pihak ketiga seperti Refit. Refit adalah pustaka REST untuk .NET. Refit memungkinkan definisi REST API deklaratif, memetakan metode antarmuka ke titik akhir. Implementasi antarmuka dihasilkan secara dinamis oleh RestService, memakai HttpClient untuk melakukan panggilan HTTP eksternal.

Pertimbangkan jenis berikut record :

namespace Shared;

public record class Todo(
    int UserId,
    int Id,
    string Title,
    bool Completed);

Contoh berikut ini bergantung pada paket NuGet Refit.HttpClientFactory, dan merupakan antarmuka sederhana:

using Refit;
using Shared;

namespace GeneratedHttp.Example;

public interface ITodoService
{
    [Get("/todos?userId={userId}")]
    Task<Todo[]> GetUserTodosAsync(int userId);
}

Antarmuka C# sebelumnya:

  • Menentukan metode bernama GetUserTodosAsync yang mengembalikan instans Task<Todo[]>.
  • Mendeklarasikan atribut Refit.GetAttribute dengan jalur dan string kueri ke API eksternal.

Klien berjenis dapat ditambahkan, menggunakan Refit untuk menghasilkan implementasi:

using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddRefitClient<ITodoService>()
    .ConfigureHttpClient(client =>
    {
        // Set the base address of the named client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

Antarmuka yang ditentukan bisa digunakan jika diperlukan, dengan implementasi yang disediakan oleh DI dan Refit.

Membuat permintaan POST, PUT, dan DELETE

Pada contoh sebelumnya, semua permintaan HTTP menggunakan kata kerja HTTP GET. HttpClient juga mendukung kata kerja HTTP lainnya, termasuk:

  • POST
  • PUT
  • DELETE
  • PATCH

Untuk daftar lengkap kata kerja HTTP yang didukung, lihat HttpMethod. Untuk informasi selengkapnya tentang membuat permintaan HTTP, lihat Mengirim permintaan menggunakan HttpClient.

Contoh berikut ini menunjukkan cara melakukan permintaan HTTP POST:

public async Task CreateItemAsync(Item item)
{
    using StringContent json = new(
        JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
        Encoding.UTF8,
        MediaTypeNames.Application.Json);

    using HttpResponseMessage httpResponse =
        await httpClient.PostAsync("/api/items", json);

    httpResponse.EnsureSuccessStatusCode();
}

Dalam kode sebelumnya, metode CreateItemAsync:

  • Menserialisasikan parameter Item ke JSON menggunakan System.Text.Json. Metode ini menggunakan instans JsonSerializerOptions untuk mengonfigurasi proses serialisasi.
  • Membuat instans StringContent untuk mengemas JSON berseri untuk dikirim di dalam isi permintaan HTTP.
  • Memanggil PostAsync untuk mengirim konten JSON ke URL yang ditentukan. Ini adalah URL relatif yang ditambahkan ke HttpClient.BaseAddress.
  • Memanggil EnsureSuccessStatusCode untuk melemparkan pengecualian jika kode status respons tidak menunjukkan keberhasilan.

HttpClient juga mendukung jenis konten lainnya. Misalnya, MultipartContent dan StreamContent. Untuk daftar lengkap konten yang didukung, lihat HttpContent.

Contoh berikut ini menunjukkan permintaan HTTP PUT:

public async Task UpdateItemAsync(Item item)
{
    using StringContent json = new(
        JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
        Encoding.UTF8,
        MediaTypeNames.Application.Json);

    using HttpResponseMessage httpResponse =
        await httpClient.PutAsync($"/api/items/{item.Id}", json);

    httpResponse.EnsureSuccessStatusCode();
}

Kode sebelumnya sangat mirip dengan contoh POST. Metode UpdateItemAsync memanggil PutAsync alih-alih PostAsync.

Contoh berikut ini menunjukkan permintaan HTTP DELETE:

public async Task DeleteItemAsync(Guid id)
{
    using HttpResponseMessage httpResponse =
        await httpClient.DeleteAsync($"/api/items/{id}");

    httpResponse.EnsureSuccessStatusCode();
}

Dalam kode sebelumnya, metode DeleteItemAsync memanggil DeleteAsync. Karena permintaan HTTP DELETE biasanya tidak memiliki isi, metode DeleteAsync ini tidak menyediakan kelebihan beban yang menerima instans HttpContent.

Untuk mempelajari selengkapnya mengenai penggunaan kata kerja HTTP yang berbeda dengan HttpClient, lihat HttpClient.

HttpClient manajemen seumur hidup

Instans HttpClient baru dikembalikan setiap kali CreateClient dipanggil pada IHttpClientFactory. Satu HttpClientHandler instans dibuat per nama klien. Pabrik mengelola masa pakai instans HttpClientHandler.

IHttpClientFactory cache instans yang HttpClientHandler dibuat oleh pabrik untuk mengurangi konsumsi sumber daya. Instans HttpClientHandler dapat digunakan kembali dari cache saat membuat instans baru HttpClient jika masa pakainya belum kedaluwarsa.

Penembolokan handler diinginkan karena setiap handler biasanya mengelola kumpulan koneksi HTTP yang mendasarnya sendiri. Membuat lebih banyak handler daripada yang diperlukan dapat mengakibatkan kelelahan soket dan penundaan koneksi. Beberapa penanganan juga menjaga koneksi tetap terbuka tanpa batas, yang dapat mencegah penanganan bereaksi terhadap perubahan DNS.

Masa pakai handler default adalah dua menit. Untuk mengambil alih nilai default, panggil SetHandlerLifetime untuk setiap klien, pada IServiceCollection:

services.AddHttpClient("Named.Client")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

Penting

HttpClientinstans yang dibuat oleh IHttpClientFactory dimaksudkan untuk berumur pendek.

  • Mendaur ulang dan membuat HttpMessageHandlerulang ketika masa pakainya kedaluwarsa sangat penting untuk IHttpClientFactory memastikan handler bereaksi terhadap perubahan DNS. HttpClient terkait dengan instans handler tertentu setelah pembuatannya, sehingga instans baru HttpClient harus diminta tepat waktu untuk memastikan klien akan mendapatkan handler yang diperbarui.

  • Membuang instans seperti HttpClient itu yang dibuat oleh pabrik tidak akan menyebabkan kelelahan soket, karena pembuangannya tidak akan memicu pembuangan HttpMessageHandler. IHttpClientFactory melacak dan membuang sumber daya yang digunakan untuk membuat HttpClient instans, khususnya HttpMessageHandler instans, segera setelah masa pakainya kedaluwarsa dan tidak HttpClient ada penggunaannya lagi.

Menjaga satu HttpClient instans tetap hidup untuk durasi panjang adalah pola umum yang dapat digunakan sebagai alternatif untuk IHttpClientFactory, namun, pola ini memerlukan pengaturan tambahan, seperti PooledConnectionLifetime. Anda dapat menggunakan klien berumur panjang dengan PooledConnectionLifetime, atau klien berumur pendek yang dibuat oleh IHttpClientFactory. Untuk informasi tentang strategi mana yang akan digunakan di aplikasi Anda, lihat Panduan untuk menggunakan klien HTTP.

Mengonfigurasi HttpMessageHandler

Anda mungkin perlu mengontrol konfigurasi HttpMessageHandler bagian dalam yang digunakan oleh klien.

IHttpClientBuilder dikembalikan saat menambahkan klien bernama atau berjenis. Metode ConfigurePrimaryHttpMessageHandler ekstensi bisa digunakan untuk menentukan delegasi pada IServiceCollection. Delegasi dipakai untuk membuat dan mengonfigurasi primer HttpMessageHandler yang digunakan oleh klien tersebut:

.ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler
    {
        AllowAutoRedirect = false,
        UseDefaultCredentials = true
    };
});

Mengonfigurasi HttClientHandler memungkinkan Anda menentukan proksi untuk instans di HttpClient antara berbagai properti handler lainnya. Untuk informasi selengkapnya, lihat Proksi per klien.

Konfigurasi tambahan

Terdapat beberapa opsi konfigurasi tambahan untuk mengontrol IHttpClientHandler:

Metode Deskripsi
AddHttpMessageHandler Menambahkan handler pesan tambahan untuk HttpClient bernama.
AddTypedClient Mengonfigurasi pengikatan antara TClient dan HttpClient bernama yang terkait dengan IHttpClientBuilder.
ConfigureHttpClient Menambahkan delegasi yang akan digunakan untuk mengonfigurasi HttpClient bernama.
ConfigureHttpMessageHandlerBuilder Menambahkan delegasi yang akan digunakan untuk mengonfigurasi handler pesan yang menggunakan HttpMessageHandlerBuilder untuk HttpClient yang bernama.
ConfigurePrimaryHttpMessageHandler Mengonfigurasi primer HttpMessageHandler dari kontainer injeksi dependensi untuk HttpClient bernama.
RedactLoggedHeaders Mengatur koleksi nama header HTTP yang nilainya harus disunting lebih dahulu sebelum masuk.
SetHandlerLifetime Mengatur lamanya waktu instans HttpMessageHandler dapat digunakan kembali. Setiap klien bernama dapat memiliki nilai masa pakai handler yang dikonfigurasi sendiri.

Menggunakan IHttpClientFactory bersama dengan SocketsHttpHandler

Implementasi SocketsHttpHandlerHttpMessageHandler ditambahkan dalam .NET Core 2.1, yang memungkinkan PooledConnectionLifetime untuk dikonfigurasi. Pengaturan ini digunakan untuk memastikan bahwa handler bereaksi terhadap perubahan DNS, jadi menggunakan SocketsHttpHandler dianggap sebagai alternatif untuk menggunakan IHttpClientFactory. Untuk informasi selengkapnya, lihat Panduan untuk menggunakan klien HTTP.

Namun, SocketsHttpHandler dan IHttpClientFactory dapat digunakan bersama-sama meningkatkan konfigurasi. Dengan menggunakan kedua API ini, Anda mendapat manfaat dari konfigurasi pada tingkat rendah (misalnya, menggunakan LocalCertificateSelectionCallback untuk pemilihan sertifikat dinamis) dan tingkat tinggi (misalnya, memanfaatkan integrasi DI dan beberapa konfigurasi klien).

Untuk menggunakan kedua API:

  1. Tentukan sebagai PrimaryHandler dan siapkan PooledConnectionLifetimeSocketsHttpHandler (misalnya, ke nilai yang sebelumnya ada di HandlerLifetime).
  2. Seperti SocketsHttpHandler yang akan menangani pengumpulan dan daur ulang koneksi, maka daur ulang handler di IHttpClientFactory tingkat tidak diperlukan lagi. Anda dapat menonaktifkannya dengan mengatur HandlerLifetime ke Timeout.InfiniteTimeSpan.
services.AddHttpClient(name)
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            PooledConnectionLifetime = TimeSpan.FromMinutes(2)
        };
    })
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime

Hindari klien yang dititik dalam layanan singleton

Saat menggunakan pendekatan klien bernama, IHttpClientFactory disuntikkan ke dalam layanan, dan HttpClient instans dibuat dengan memanggil CreateClient setiap kali HttpClient diperlukan.

Namun, dengan pendekatan klien yang ditik, klien yang ditik adalah objek sementara yang biasanya disuntikkan ke dalam layanan. Itu dapat menyebabkan masalah karena klien yang ditik dapat disuntikkan ke dalam layanan singleton.

Penting

Klien yang ditik diharapkan berumur pendek dalam arti yang sama dengan HttpClient instans yang dibuat oleh IHttpClientFactory (untuk informasi selengkapnya, lihat HttpClient manajemen seumur hidup). Segera setelah instans klien yang diketik dibuat, IHttpClientFactory tidak memiliki kontrol atasnya. Jika instans klien yang ditik diambil dalam satuton, instans klien dapat mencegahnya bereaksi terhadap perubahan DNS, mengalahkan salah satu tujuan .IHttpClientFactory

Jika Anda perlu menggunakan HttpClient instans dalam layanan singleton, pertimbangkan opsi berikut:

  • Gunakan pendekatan klien bernama sebagai gantinya, menyuntikkan IHttpClientFactory layanan singleton dan membuat HttpClient ulang instans bila perlu.
  • Jika Anda memerlukan pendekatan klien yang ditik, gunakan SocketsHttpHandler dengan dikonfigurasi PooledConnectionLifetime sebagai handler utama. Untuk informasi selengkapnya tentang menggunakan SocketsHttpHandler dengan IHttpClientFactory, lihat bagian Menggunakan IHttpClientFactory bersama dengan SocketsHttpHandler.

Cakupan Handler Pesan di IHttpClientFactory

IHttpClientFactory membuat cakupan DI terpisah per setiap HttpMessageHandler instans. Cakupan DI ini terpisah dari cakupan DI aplikasi (misalnya, ASP.NET cakupan permintaan masuk, atau cakupan DI manual yang dibuat pengguna), sehingga mereka tidak akan berbagi instans layanan tercakup. Cakupan Handler Pesan terkait dengan masa pakai handler dan dapat mengeluar cakupan aplikasi, yang dapat menyebabkan, misalnya, menggunakan kembali instans yang sama HttpMessageHandler dengan dependensi tercakup yang disuntikkan yang sama antara beberapa permintaan masuk.

Diagram memperlihatkan dua cakupan DI aplikasi dan cakupan handler pesan terpisah

Pengguna sangat disarankan untuk tidak menyimpan informasi terkait cakupan (seperti data dari HttpContext) di dalam HttpMessageHandler instans dan menggunakan dependensi terlingkup dengan hati-hati untuk menghindari kebocoran informasi sensitif.

Jika Anda memerlukan akses ke cakupan DI aplikasi dari handler pesan Anda, untuk autentikasi sebagai contoh, Anda akan merangkum logika sadar cakupan dalam sementara DelegatingHandlerterpisah, dan membungkusnya di sekitar HttpMessageHandler instans dari IHttpClientFactory cache. Untuk mengakses panggilan IHttpMessageHandlerFactory.CreateHandler handler untuk setiap klien bernama terdaftar. Dalam hal ini, Anda akan membuat HttpClient instans sendiri menggunakan handler yang dibangun.

Diagram memperlihatkan mendapatkan akses ke cakupan DI aplikasi melalui handler pesan sementara terpisah dan IHttpMessageHandlerFactory

Contoh berikut menunjukkan pembuatan HttpClient dengan cakupan sadar DelegatingHandlercakupan :

if (scopeAwareHandlerType != null)
{
    if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
    {
        throw new ArgumentException($"""
            Scope aware HttpHandler {scopeAwareHandlerType.Name} should
            be assignable to DelegatingHandler
            """);
    }

    // Create top-most delegating handler with scoped dependencies
    scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
    if (scopeAwareHandler.InnerHandler != null)
    {
        throw new ArgumentException($"""
            Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
            Scope aware HttpHandler should be registered as Transient.
            """);
    }
}

// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);

if (scopeAwareHandler != null)
{
    scopeAwareHandler.InnerHandler = handler;
    handler = scopeAwareHandler;
}

HttpClient client = new(handler);

Solusi lebih lanjut dapat diikuti dengan metode ekstensi untuk mendaftarkan pendaftaran default IHttpClientFactory yang sadar DelegatingHandler cakupan dan mengambil alih oleh layanan sementara dengan akses ke cakupan aplikasi saat ini:

public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
    this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
    builder.Services.TryAddTransient<THandler>();
    if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
    {
        // Override default IHttpClientFactory registration
        builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
    }

    builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
        builder.Name, options => options.HttpHandlerType = typeof(THandler));

    return builder;
}

Untuk informasi selengkapnya, lihat contoh lengkapnya.

Lihat juga