Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Note
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat artikel ini dalam versi .NET 10.
Warning
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan Inti .NET dan .NET. Untuk rilis saat ini, lihat artikel ini dalam versi .NET 10.
Oleh Kirk Larkin, Steve Gordon, Glenn Condron, dan Ryan Nowak.
IHttpClientFactory dapat didaftarkan dan digunakan untuk mengonfigurasi dan membuat HttpClient instance di dalam aplikasi.
IHttpClientFactory menawarkan manfaat berikut:
- Menyediakan lokasi pusat untuk penamaan dan konfigurasi instans
HttpClientlogis. Misalnya, klien bernama github dapat didaftarkan dan dikonfigurasi untuk mengakses GitHub. Klien default dapat didaftarkan untuk akses umum. - Mengkodifikasi konsep middleware outbound melalui pendelegasian handler di
HttpClient. Menyediakan ekstensi untuk middleware berbasis Polly untuk memanfaatkan pendelegasian handler diHttpClient. - Mengelola pengumpulan dan masa pakai instans
HttpClientMessageHandleryang mendasarinya. Manajemen otomatis menghindari masalah DNS umum (Sistem Nama Domain) yang terjadi saat mengelolaHttpClientmasa pakai secara manual. - Menambahkan pengalaman pengelogan yang bisa dikonfigurasi (melalui
ILogger) untuk semua permintaan yang dikirim melalui klien yang dibuat oleh pabrik.
Kode sampel dalam versi topik ini menggunakan System.Text.Json untuk mendeserialisasi konten JSON yang dikembalikan dalam respons HTTP. Untuk sampel yang menggunakan Json.NET dan ReadAsAsync<T>, gunakan pemilih versi untuk memilih versi 2.x dari topik ini.
Pola konsumsi
Ada beberapa cara menggunakan IHttpClientFactory dalam aplikasi:
Pendekatan terbaiknya tergantung pada persyaratan aplikasi.
Penggunaan dasar
Daftar IHttpClientFactory dengan memanggil AddHttpClient di Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddHttpClient();
Sebuah IHttpClientFactory dapat diminta menggunakan injeksi dependensi (DI). Kode berikut ini menggunakan IHttpClientFactory untuk membuat instansHttpClient:
public class BasicModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
public BasicModel(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
var httpRequestMessage = new HttpRequestMessage(
HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
{
Headers =
{
{ HeaderNames.Accept, "application/vnd.github.v3+json" },
{ HeaderNames.UserAgent, "HttpRequestsSample" }
}
};
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
if (httpResponseMessage.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
GitHubBranches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
}
}
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
HttpClientyang berbeda. - Banyak
HttpClientyang memiliki konfigurasi yang berbeda.
Tentukan konfigurasi untuk HttpClient yang bernama tertentu selama pendaftarannya di Program.cs:
builder.Services.AddHttpClient("GitHub", httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
});
Dalam kode sebelumnya, klien dikonfigurasi dengan:
- Alamat dasar
https://api.github.com/. - Dua header yang diperlukan untuk bekerja dengan API GitHub.
CreateClient
Setiap kali CreateClient disebut:
- Instans
HttpClientbaru dibuat. - Tindakan konfigurasi dijalankan.
Untuk membuat klien bernama, berikan namanya ke dalam CreateClient:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
public NamedClientModel(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
var httpClient = _httpClientFactory.CreateClient("GitHub");
var httpResponseMessage = await httpClient.GetAsync(
"repos/dotnet/AspNetCore.Docs/branches");
if (httpResponseMessage.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
GitHubBranches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
}
}
Dalam kode sebelumnya, permintaan tidak perlu menentukan nama host. Kode hanya bisa melewati jalur karena alamat dasar yang dikonfigurasi untuk klien digunakan.
Klien yang dititik
Klien yang dititik:
- Memberikan kemampuan yang sama seperti klien bernama tanpa perlu menggunakan string sebagai kunci.
- Menyediakan bantuan IntelliSense dan bantuan dari compiler saat menggunakan klien.
- Menyediakan satu lokasi untuk mengonfigurasi dan berinteraksi dengan
HttpClienttertentu. Misalnya, klien bertipe 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 bertipe menerima parameter HttpClient di dalam konstruktornya.
public class GitHubService
{
private readonly HttpClient _httpClient;
public GitHubService(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
_httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
_httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
}
public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync() =>
await _httpClient.GetFromJsonAsync<IEnumerable<GitHubBranch>>(
"repos/dotnet/AspNetCore.Docs/branches");
}
Dalam kode sebelumnya:
- Konfigurasi dipindahkan ke klien yang ditik.
- Instans yang disediakan
HttpClientdisimpan sebagai bidang privat.
Metode khusus API bisa dibuat yang mengekspos fungsionalitas HttpClient. Misalnya, metode GetAspNetCoreDocsBranches merangkum kode untuk mengambil dokumen GitHub cabang.
Kode berikut memanggil
builder.Services.AddHttpClient<GitHubService>();
Klien bertipe didaftarkan sebagai transien di dalam DI. Dalam kode sebelumnya, AddHttpClient mendaftarkan GitHubService sebagai layanan sementara. Pendaftaran ini menggunakan metode pabrik untuk:
- Buat instans
HttpClient. - Membuat instans
GitHubService, meneruskan instansHttpClientke konstruktornya.
Klien terketik dapat langsung disuntikkan dan digunakan.
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public TypedClientModel(GitHubService gitHubService) =>
_gitHubService = gitHubService;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
try
{
GitHubBranches = await _gitHubService.GetAspNetCoreDocsBranchesAsync();
}
catch (HttpRequestException)
{
// ...
}
}
}
Konfigurasi untuk klien bertipe juga dapat ditentukan saat pendaftarannya di Program.cs, bukan melalui konstruktor klien bertipe.
builder.Services.AddHttpClient<GitHubService>(httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// ...
});
Klien yang dihasilkan
IHttpClientFactory bisa digunakan dalam kombinasi dengan pustaka pihak ketiga seperti Refit. Refit adalah pustaka REST untuk .NET. Ini mengonversi REST API menjadi antarmuka langsung. Panggilan AddRefitClient untuk menghasilkan implementasi dinamis antarmuka, yang menggunakan HttpClient untuk melakukan panggilan HTTP eksternal.
Antarmuka kustom mewakili API eksternal:
public interface IGitHubClient
{
[Get("/repos/dotnet/AspNetCore.Docs/branches")]
Task<IEnumerable<GitHubBranch>> GetAspNetCoreDocsBranchesAsync();
}
Panggil AddRefitClient untuk menghasilkan implementasi dinamis lalu panggil ConfigureHttpClient untuk mengonfigurasi yang mendasar HttpClient:
builder.Services.AddRefitClient<IGitHubClient>()
.ConfigureHttpClient(httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
});
Gunakan DI untuk mengakses implementasi dinamis dari IGitHubClient:
public class RefitModel : PageModel
{
private readonly IGitHubClient _gitHubClient;
public RefitModel(IGitHubClient gitHubClient) =>
_gitHubClient = gitHubClient;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
try
{
GitHubBranches = await _gitHubClient.GetAspNetCoreDocsBranchesAsync();
}
catch (ApiException)
{
// ...
}
}
}
Membuat permintaan POST, PUT, dan DELETE
Dalam 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.
Contoh berikut menunjukkan cara membuat permintaan HTTP POST:
public async Task CreateItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
Application.Json); // using static System.Net.Mime.MediaTypeNames;
using var httpResponseMessage =
await _httpClient.PostAsync("/api/TodoItems", todoItemJson);
httpResponseMessage.EnsureSuccessStatusCode();
}
Dalam kode sebelumnya, metode CreateItemAsync:
- Menserialisasikan parameter
TodoItemke JSON menggunakanSystem.Text.Json. - 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 melempar 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 menunjukkan permintaan HTTP PUT:
public async Task SaveItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
Application.Json);
using var httpResponseMessage =
await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);
httpResponseMessage.EnsureSuccessStatusCode();
}
Kode sebelumnya mirip dengan contoh POST. Metode SaveItemAsync memanggil PutAsync alih-alih PostAsync.
Contoh berikut menunjukkan permintaan HTTP DELETE:
public async Task DeleteItemAsync(long itemId)
{
using var httpResponseMessage =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
httpResponseMessage.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.
Middleware untuk permintaan yang keluar
HttpClient memiliki konsep mendelegasikan handler yang dapat ditautkan bersama untuk permintaan HTTP keluar.
IHttpClientFactory:
- Menyederhanakan penentuan handler yang diterapkan pada setiap klien dengan nama tertentu.
- Mendukung pendaftaran dan penautan beberapa handler untuk membangun alur middleware permintaan keluar. Masing-masing handler ini dapat melakukan tugas sebelum dan sesudah permintaan keluar. Pola ini:
- Mirip dengan alur middleware masuk di ASP.NET Core.
- Menyediakan mekanisme untuk mengelola concern lintas dalam permintaan HTTP, seperti:
- penembolokan
- penanganan kesalahan
- serialization
- pengelogan
Untuk membuat pengendali pendelegasian:
- Berasal dari DelegatingHandler.
- Ambil alih SendAsync. Jalankan kode sebelum meneruskan permintaan ke handler berikutnya dalam alur:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"The API key header X-API-KEY is required.")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Kode sebelumnya memeriksa apakah X-API-KEY header ada dalam permintaan. Jika X-API-KEY hilang, BadRequest dikembalikan.
Lebih dari satu handler dapat ditambahkan ke konfigurasi untuk HttpClient dengan Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:
builder.Services.AddTransient<ValidateHeaderHandler>();
builder.Services.AddHttpClient("HttpMessageHandler")
.AddHttpMessageHandler<ValidateHeaderHandler>();
Dalam kode sebelumnya, ValidateHeaderHandler terdaftar di DI. Setelah terdaftar, AddHttpMessageHandler dapat dipanggil, dengan meneruskan jenis data untuk pengelola.
Beberapa handler dapat didaftarkan dalam urutan yang harus mereka jalankan. Setiap handler membungkus handler berikutnya hingga final HttpClientHandler menjalankan permintaan:
builder.Services.AddTransient<SampleHandler1>();
builder.Services.AddTransient<SampleHandler2>();
builder.Services.AddHttpClient("MultipleHttpMessageHandlers")
.AddHttpMessageHandler<SampleHandler1>()
.AddHttpMessageHandler<SampleHandler2>();
Dalam kode sebelumnya, SampleHandler1 berjalan terlebih dahulu, sebelum SampleHandler2.
Menggunakan DI dalam middleware permintaan keluar
Saat IHttpClientFactory membuat handler pendelegasian baru, ia menggunakan DI untuk memenuhi parameter konstruktor handler.
IHttpClientFactory
membuat cakupan DI terpisah untuk setiap handler, yang dapat menyebabkan perilaku mengejutkan ketika handler mengonsumsi layanan tercakup.
Misalnya, pertimbangkan antarmuka berikut dan implementasinya, yang mewakili tugas sebagai operasi dengan pengidentifikasi, OperationId:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Seperti namanya, IOperationScoped terdaftar di DI menggunakan masa pakai tercakup:
builder.Services.AddScoped<IOperationScoped, OperationScoped>();
Penangan delegasi IOperationScoped berikut menggunakan IOperationScoped untuk mengatur tajuk untuk permintaan keluar.
public class OperationHandler : DelegatingHandler
{
private readonly IOperationScoped _operationScoped;
public OperationHandler(IOperationScoped operationScoped) =>
_operationScoped = operationScoped;
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-OPERATION-ID", _operationScoped.OperationId);
return await base.SendAsync(request, cancellationToken);
}
}
Dalam unduhanHttpRequestsSample, navigasikan ke /Operation dan refresh halaman. Nilai cakupan permintaan berubah untuk setiap permintaan, tetapi nilai cakupan handler hanya berubah setiap 5 detik.
Handler dapat bergantung pada layanan dari ruang lingkup mana pun. Layanan yang bergantung pada handler dibersihkan ketika handler dibersihkan.
Gunakan salah satu pendekatan berikut untuk berbagi status per permintaan dengan penangan pesan:
- Teruskan data ke handler menggunakan HttpRequestMessage.Options.
- Gunakan IHttpContextAccessor untuk mengakses permintaan saat ini.
- Buat objek penyimpanan kustom AsyncLocal<T> untuk meneruskan data.
Menggunakan handler berbasis Polly
IHttpClientFactory terintegrasi dengan pustaka pihak ketiga Polly. Polly adalah pustaka komprehensif untuk penanganan kesalahan sementara dan ketahanan pada .NET. Ini memungkinkan pengembang untuk mengekspresikan kebijakan seperti Retry, Circuit Breaker, Timeout, Bulkhead Isolation, dan Fallback dengan cara yang lancar dan aman utas.
Metode ekstensi disediakan untuk memungkinkan penerapan kebijakan Polly pada instans HttpClient yang telah dikonfigurasi. Ekstensi Polly mendukung penambahan handler berbasis Polly ke klien. Polly membutuhkan Microsoft. Extensions.Http.Polly paket NuGet.
Menangani kesalahan sementara
Kesalahan biasanya terjadi ketika panggilan HTTP eksternal bersifat sementara.
AddTransientHttpErrorPolicy memungkinkan kebijakan didefinisikan untuk menangani kesalahan sementara. Kebijakan yang dikonfigurasi dengan AddTransientHttpErrorPolicy menangani respons berikut:
- HttpRequestException
- HTTP 5xx
- HTTP 408
AddTransientHttpErrorPolicy menyediakan akses ke objek yang dikonfigurasi PolicyBuilder untuk menangani kesalahan yang mewakili kemungkinan kesalahan sementara:
builder.Services.AddHttpClient("PollyWaitAndRetry")
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.WaitAndRetryAsync(
3, retryNumber => TimeSpan.FromMilliseconds(600)));
Dalam kode sebelumnya, suatu kebijakan WaitAndRetryAsync ditentukan. Permintaan yang gagal dicoba ulang hingga tiga kali dengan penundaan 600 ms antar upaya.
Memilih kebijakan secara dinamis
Metode ekstensi disediakan untuk menambahkan handler berbasis Polly, misalnya, AddPolicyHandler. Kelebihan beban berikut AddPolicyHandler memeriksa permintaan untuk memutuskan kebijakan mana yang akan diterapkan:
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
builder.Services.AddHttpClient("PollyDynamic")
.AddPolicyHandler(httpRequestMessage =>
httpRequestMessage.Method == HttpMethod.Get ? timeoutPolicy : longTimeoutPolicy);
Dalam kode sebelumnya, jika permintaan keluar adalah HTTP GET, batas waktu 10 detik diterapkan. Untuk metode HTTP lainnya, batas waktu 30 detik digunakan.
Menambahkan beberapa handler Polly
Adalah umum untuk menyusun kebijakan Polly:
builder.Services.AddHttpClient("PollyMultiple")
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.RetryAsync(3))
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Dalam contoh sebelumnya:
- Dua handler ditambahkan.
- Handler pertama menggunakan AddTransientHttpErrorPolicy untuk menambahkan kebijakan coba ulang. Permintaan yang gagal dicoba ulang hingga tiga kali.
- Panggilan kedua
AddTransientHttpErrorPolicymenambahkan kebijakan pemutus sirkuit. Permintaan eksternal lebih lanjut diblokir selama 30 detik jika 5 upaya gagal terjadi secara berurutan. Kebijakan pemutus sirkuit bersifat stateful. Semua panggilan melalui klien ini memiliki status sirkuit yang sama.
Menambahkan kebijakan dari registri Polly
Pendekatan untuk mengelola kebijakan yang digunakan secara teratur adalah menentukannya sekali dan mendaftarkannya dengan PolicyRegistry. Contohnya:
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
var policyRegistry = builder.Services.AddPolicyRegistry();
policyRegistry.Add("Regular", timeoutPolicy);
policyRegistry.Add("Long", longTimeoutPolicy);
builder.Services.AddHttpClient("PollyRegistryRegular")
.AddPolicyHandlerFromRegistry("Regular");
builder.Services.AddHttpClient("PollyRegistryLong")
.AddPolicyHandlerFromRegistry("Long");
Dalam kode sebelumnya:
- Dua kebijakan,
RegulardanLong, ditambahkan ke registri Polly. - AddPolicyHandlerFromRegistry mengonfigurasi klien individu yang diberi nama untuk menggunakan kebijakan ini dari registri Polly.
Untuk informasi selengkapnya tentang IHttpClientFactory dan integrasi Polly, lihat wiki Polly.
HttpClient dan manajemen siklus hidup
Instans HttpClient baru dikembalikan setiap kali CreateClient dipanggil pada IHttpClientFactory.
HttpMessageHandler Dibuat untuk setiap klien yang diberi nama. Pabrik mengelola masa pakai instans HttpMessageHandler.
IHttpClientFactory mengumpulkan instans HttpMessageHandler yang dibuat oleh pabrik untuk mengurangi konsumsi sumber daya. Instans HttpMessageHandler dapat digunakan kembali dari pool saat membuat instans HttpClient baru jika masa pakainya belum kedaluwarsa.
Pengelompokan handler diinginkan karena setiap handler biasanya mengelola koneksi HTTP yang mendasari secara mandiri. Membuat lebih banyak handler daripada yang dibutuhkan dapat mengakibatkan penundaan koneksi. Beberapa handler juga menjaga koneksi tetap terbuka tanpa batas waktu, yang dapat mencegah handler bereaksi terhadap perubahan DNS (Domain Name System).
Masa pakai handler default adalah dua menit. Nilai default dapat diganti untuk setiap klien yang diberi nama.
builder.Services.AddHttpClient("HandlerLifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
instans HttpClient umumnya dapat diperlakukan sebagai objek .NET tidak memerlukan penghapusan. Proses pembuangan membatalkan permintaan keluar dan menjamin instans HttpClient yang diberikan tidak dapat digunakan setelah memanggil Dispose.
IHttpClientFactory melacak dan membuang sumber daya yang digunakan oleh instans HttpClient.
Menjaga satu instans HttpClient tetap hidup untuk durasi yang lama adalah pola umum yang digunakan sebelum adanya IHttpClientFactory. Pola ini menjadi tidak perlu setelah bermigrasi ke IHttpClientFactory.
Alternatif untuk IHttpClientFactory
Menggunakan IHttpClientFactory dalam aplikasi yang diaktifkan DI menghindari:
- Masalah kelelahan sumber daya karena pengumpulan instans
HttpMessageHandler. - Mengatasi masalah DNS yang kedaluwarsa dengan mereset
HttpMessageHandlerinstans secara berkala.
Ada cara alternatif untuk menyelesaikan masalah sebelumnya menggunakan instans SocketsHttpHandler yang berusia panjang.
- Buat instans
SocketsHttpHandlersaat aplikasi dimulai dan gunakan untuk masa pakai aplikasi. - Konfigurasikan PooledConnectionLifetime ke nilai yang sesuai berdasarkan waktu refresh DNS.
- Buat
HttpClientinstance menggunakannew HttpClient(handler, disposeHandler: false)sesuai kebutuhan.
Pendekatan sebelumnya menyelesaikan masalah manajemen sumber daya yang IHttpClientFactory diselesaikan dengan cara yang sama.
- Koneksi
SocketsHttpHandlerberbagi antar instansHttpClient. Berbagi ini mencegah kelelahan soket. - Siklus
SocketsHttpHandlerkoneksi sesuaiPooledConnectionLifetimeuntuk menghindari masalah DNS usang.
Logging
Klien yang dibuat melalui IHttpClientFactory mencatat pesan log untuk semua permintaan. Aktifkan tingkat informasi yang sesuai dalam konfigurasi pengelogan untuk melihat pesan log default. Pengelogan tambahan, seperti pengelogan header permintaan, hanya disertakan pada tingkat pelacakan.
Kategori log yang digunakan untuk setiap klien menyertakan nama klien. Klien bernama MyNamedClient, misalnya, mencatat pesan dengan kategori "System.Net.Http.HttpClient.MyNamedClient. LogicalHandler". Pesan yang dibubuhi dengan LogicalHandler terjadi di luar pipeline penanganan permintaan. Pada saat permintaan, pesan dicatat sebelum penangan lain dalam jalur telah memprosesnya. Pada respons, pesan dicatat setelah handler alur lain menerima respons.
Pengelogan juga terjadi di dalam alur handler permintaan. Dalam contoh MyNamedClient, pesan tersebut dicatat dengan kategori log "System.Net.Http.Http.HttpClient.MyNamedClient. ClientHandler". Untuk permintaan, ini terjadi setelah semua handler lain berjalan dan segera sebelum permintaan dikirim. Pada respons, pengelogan ini mencakup status respons sebelum melewati kembali melalui alur handler.
Mengaktifkan pengelogan di luar dan di dalam alur memungkinkan inspeksi perubahan yang dilakukan oleh penangan alur lainnya. Ini dapat mencakup perubahan pada header permintaan atau ke kode status respons.
Menyertakan nama klien dalam kategori log memungkinkan pemfilteran log untuk klien bernama tertentu.
Mengonfigurasi HttpMessageHandler
Anda mungkin perlu mengontrol konfigurasi HttpMessageHandler bagian dalam yang digunakan oleh klien.
IHttpClientBuilder dikembalikan saat menambahkan klien bernama atau berjenis. Metode ekstensi ConfigurePrimaryHttpMessageHandler dapat digunakan untuk menentukan delegat. Delegasi dipakai untuk membuat dan mengonfigurasi primer HttpMessageHandler yang digunakan oleh klien tersebut:
builder.Services.AddHttpClient("ConfiguredHttpMessageHandler")
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler
{
AllowAutoRedirect = true,
UseDefaultCredentials = true
});
Cookies
Instans yang dikumpulkan HttpMessageHandler menghasilkan CookieContainer objek yang dibagikan. Berbagi objek yang tidak diantisipasi CookieContainer sering kali menghasilkan kode yang tidak benar. Untuk aplikasi yang memerlukan cookie, pertimbangkan:
- Menonaktifkan penanganan otomatis cookie
- Menghindari
IHttpClientFactory
Panggilan ConfigurePrimaryHttpMessageHandler untuk menonaktifkan penanganan otomatis cookie :
builder.Services.AddHttpClient("NoAutomaticCookies")
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler
{
UseCookies = false
});
Menggunakan IHttpClientFactory di aplikasi konsol
Di aplikasi konsol, tambahkan referensi paket berikut ke proyek:
Dalam contoh berikut:
-
IHttpClientFactory dan
GitHubServiceterdaftar dalam kontainer layanan Host Generik. -
GitHubServicediminta dari DI, yang kemudian meminta instansIHttpClientFactory. -
GitHubServicemenggunakanIHttpClientFactoryuntuk membuat instansHttpClient, yang digunakannya untuk mengambil dokumen GitHub cabang.
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var host = new HostBuilder()
.ConfigureServices(services =>
{
services.AddHttpClient();
services.AddTransient<GitHubService>();
})
.Build();
try
{
var gitHubService = host.Services.GetRequiredService<GitHubService>();
var gitHubBranches = await gitHubService.GetAspNetCoreDocsBranchesAsync();
Console.WriteLine($"{gitHubBranches?.Count() ?? 0} GitHub Branches");
if (gitHubBranches is not null)
{
foreach (var gitHubBranch in gitHubBranches)
{
Console.WriteLine($"- {gitHubBranch.Name}");
}
}
}
catch (Exception ex)
{
host.Services.GetRequiredService<ILogger<Program>>()
.LogError(ex, "Unable to load branches from GitHub.");
}
public class GitHubService
{
private readonly IHttpClientFactory _httpClientFactory;
public GitHubService(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync()
{
var httpRequestMessage = new HttpRequestMessage(
HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
{
Headers =
{
{ "Accept", "application/vnd.github.v3+json" },
{ "User-Agent", "HttpRequestsConsoleSample" }
}
};
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
httpResponseMessage.EnsureSuccessStatusCode();
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
}
public record GitHubBranch(
[property: JsonPropertyName("name")] string Name);
Middleware penyebaran header
Propagasi header adalah middleware ASP.NET Core untuk meneruskan header HTTP dari permintaan masuk ke permintaan HttpClient keluar. Untuk menggunakan penyebaran header:
Instal paket Microsoft.AspNetCore.HeaderPropagation.
Konfigurasikan
HttpClientdan alur middleware diProgram.cs:// Add services to the container. builder.Services.AddControllers(); builder.Services.AddHttpClient("PropagateHeaders") .AddHeaderPropagation(); builder.Services.AddHeaderPropagation(options => { options.Headers.Add("X-TraceId"); }); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseHttpsRedirection(); app.UseHeaderPropagation(); app.MapControllers();Buat permintaan eksternal menggunakan instans
HttpClientyang telah dikonfigurasi, yang mencakup header tambahan.
Sumber daya tambahan
- Melihat atau mengunduh kode sampel (cara mengunduh)
- Gunakan HttpClientFactory untuk menerapkan permintaan HTTP yang tangguh
- Menerapkan percobaan ulang panggilan HTTP dengan backoff eksponensial dengan kebijakan HttpClientFactory dan Polly
- Menerapkan pola Circuit Breaker
- Cara membuat serialisasi dan mendeserialisasi JSON di .NET
Oleh Kirk Larkin, Steve Gordon, Glenn Condron, dan Ryan Nowak.
IHttpClientFactory dapat didaftarkan dan digunakan untuk mengonfigurasi dan membuat HttpClient instance di dalam aplikasi.
IHttpClientFactory menawarkan manfaat berikut:
- Menyediakan lokasi pusat untuk penamaan dan konfigurasi instans
HttpClientlogis. Misalnya, klien bernama github dapat didaftarkan dan dikonfigurasi untuk mengakses GitHub. Klien default dapat didaftarkan untuk akses umum. - Mengkodifikasi konsep middleware outbound melalui pendelegasian handler di
HttpClient. Menyediakan ekstensi untuk middleware berbasis Polly untuk memanfaatkan pendelegasian handler diHttpClient. - Mengelola pengumpulan dan masa pakai instans
HttpClientMessageHandleryang mendasarinya. Manajemen otomatis menghindari masalah DNS umum (Sistem Nama Domain) yang terjadi saat mengelolaHttpClientmasa pakai secara manual. - Menambahkan pengalaman pengelogan yang bisa dikonfigurasi (melalui
ILogger) untuk semua permintaan yang dikirim melalui klien yang dibuat oleh pabrik.
Lihat atau unduh sampel kode (cara mengunduh).
Kode sampel dalam versi topik ini menggunakan System.Text.Json untuk mendeserialisasi konten JSON yang dikembalikan dalam respons HTTP. Untuk sampel yang menggunakan Json.NET dan ReadAsAsync<T>, gunakan pemilih versi untuk memilih versi 2.x dari topik ini.
Pola konsumsi
Ada beberapa cara menggunakan IHttpClientFactory dalam aplikasi:
Pendekatan terbaiknya tergantung pada persyaratan aplikasi.
Penggunaan dasar
IHttpClientFactory dapat didaftarkan dengan memanggil AddHttpClient:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// Remaining code deleted for brevity.
Sebuah IHttpClientFactory dapat diminta menggunakan injeksi dependensi (DI). Kode berikut ini menggunakan IHttpClientFactory untuk membuat instansHttpClient:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubBranch> Branches { get; private set; }
public bool GetBranchesError { get; private set; }
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
Branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
GetBranchesError = true;
Branches = Array.Empty<GitHubBranch>();
}
}
}
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
HttpClientyang berbeda. - Banyak
HttpClientyang memiliki konfigurasi yang berbeda.
Konfigurasi untuk HttpClient yang bernama dapat ditentukan selama pendaftaran di Startup.ConfigureServices:
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Dalam kode sebelumnya, klien dikonfigurasi dengan:
- Alamat dasar
https://api.github.com/. - Dua header yang diperlukan untuk bekerja dengan API GitHub.
CreateClient
Setiap kali CreateClient disebut:
- Instans
HttpClientbaru dibuat. - Tindakan konfigurasi dijalankan.
Untuk membuat klien bernama, berikan namanya ke dalam CreateClient:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }
public bool GetPullRequestsError { get; private set; }
public bool HasPullRequests => PullRequests.Any();
public NamedClientModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"repos/dotnet/AspNetCore.Docs/pulls");
var client = _clientFactory.CreateClient("github");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
PullRequests = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubPullRequest>>(responseStream);
}
else
{
GetPullRequestsError = true;
PullRequests = Array.Empty<GitHubPullRequest>();
}
}
}
Dalam kode sebelumnya, permintaan tidak perlu menentukan nama host. Kode hanya bisa melewati jalur karena alamat dasar yang dikonfigurasi untuk klien digunakan.
Klien yang dititik
Klien yang dititik:
- Memberikan kemampuan yang sama seperti klien bernama tanpa perlu menggunakan string sebagai kunci.
- Menyediakan bantuan IntelliSense dan bantuan dari compiler saat menggunakan klien.
- Menyediakan satu lokasi untuk mengonfigurasi dan berinteraksi dengan
HttpClienttertentu. Misalnya, klien bertipe 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 bertipe menerima parameter HttpClient di dalam konstruktornya.
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
return await Client.GetFromJsonAsync<IEnumerable<GitHubIssue>>(
"/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
}
}
Dalam kode sebelumnya:
- Konfigurasi dipindahkan ke klien yang ditik.
- Objek
HttpClientdiekspos sebagaipublicproperti.
Metode khusus API bisa dibuat yang mengekspos fungsionalitas HttpClient. Misalnya, metode GetAspNetDocsIssues mengenkapsulasi kode untuk mengambil masalah terbuka.
Kode berikut ini memanggil AddHttpClient pada Startup.ConfigureServices untuk mendaftarkan kelas klien berjenis:
services.AddHttpClient<GitHubService>();
Klien bertipe didaftarkan sebagai transien di dalam DI. Dalam kode sebelumnya, AddHttpClient mendaftarkan GitHubService sebagai layanan sementara. Pendaftaran ini menggunakan metode pabrik untuk:
- Buat instans
HttpClient. - Membuat instans
GitHubService, meneruskan instansHttpClientke konstruktornya.
Klien terketik dapat langsung disuntikkan dan digunakan.
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public IEnumerable<GitHubIssue> LatestIssues { get; private set; }
public bool HasIssue => LatestIssues.Any();
public bool GetIssuesError { get; private set; }
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
catch(HttpRequestException)
{
GetIssuesError = true;
LatestIssues = Array.Empty<GitHubIssue>();
}
}
}
Konfigurasi untuk klien yang ditik dapat ditentukan selama pendaftaran di Startup.ConfigureServices, bukan di konstruktor klien yang ditik:
services.AddHttpClient<RepoService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
HttpClient dapat dienkapsulasi dalam klien yang ditik. Daripada mengeksposnya sebagai properti, tentukan metode yang memanggil instans HttpClient secara internal:
public class RepoService
{
// _httpClient isn't exposed publicly
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();
return await JsonSerializer.DeserializeAsync
<IEnumerable<string>>(responseStream);
}
}
Dalam kode sebelumnya, HttpClient disimpan di bidang privat. Akses ke HttpClient adalah dengan metode publik GetRepos .
Klien yang dihasilkan
IHttpClientFactory bisa digunakan dalam kombinasi dengan pustaka pihak ketiga seperti Refit. Refit adalah pustaka REST untuk .NET. Ini mengonversi REST API menjadi antarmuka langsung. Implementasi antarmuka dihasilkan secara dinamis oleh RestService, memakai HttpClient untuk melakukan panggilan HTTP eksternal.
Antarmuka dan balasan didefinisikan untuk mewakili API eksternal dan responsnya:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
"Klien dengan tipe dapat ditambahkan, menggunakan Refit untuk membuat implementasi:"
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddControllers();
}
Antarmuka yang ditentukan dapat dikonsumsi jika perlu, dengan implementasi yang disediakan oleh DI dan Refit:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
Membuat permintaan POST, PUT, dan DELETE
Dalam 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.
Contoh berikut menunjukkan cara membuat permintaan HTTP POST:
public async Task CreateItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem, _jsonSerializerOptions),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PostAsync("/api/TodoItems", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Dalam kode sebelumnya, metode CreateItemAsync:
- Menserialisasikan parameter
TodoItemke JSON menggunakanSystem.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 menunjukkan permintaan HTTP PUT:
public async Task SaveItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Kode sebelumnya sangat mirip dengan contoh POST. Metode SaveItemAsync memanggil PutAsync alih-alih PostAsync.
Contoh berikut menunjukkan permintaan HTTP DELETE:
public async Task DeleteItemAsync(long itemId)
{
using var httpResponse =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
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.
Middleware untuk permintaan yang keluar
HttpClient memiliki konsep mendelegasikan handler yang dapat ditautkan bersama untuk permintaan HTTP keluar.
IHttpClientFactory:
- Menyederhanakan penentuan handler yang diterapkan pada setiap klien dengan nama tertentu.
- Mendukung pendaftaran dan penautan beberapa handler untuk membangun alur middleware permintaan keluar. Masing-masing handler ini dapat melakukan tugas sebelum dan sesudah permintaan keluar. Pola ini:
- Mirip dengan alur middleware masuk di ASP.NET Core.
- Menyediakan mekanisme untuk mengelola concern lintas dalam permintaan HTTP, seperti:
- penembolokan
- penanganan kesalahan
- serialization
- pengelogan
Untuk membuat pengendali pendelegasian:
- Berasal dari DelegatingHandler.
- Ambil alih SendAsync. Jalankan kode sebelum meneruskan permintaan ke handler berikutnya dalam alur:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Kode sebelumnya memeriksa apakah X-API-KEY header ada dalam permintaan. Jika X-API-KEY hilang, BadRequest dikembalikan.
Lebih dari satu handler dapat ditambahkan ke konfigurasi untuk HttpClient dengan Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
// Remaining code deleted for brevity.
Dalam kode sebelumnya, ValidateHeaderHandler terdaftar di DI. Setelah terdaftar, AddHttpMessageHandler dapat dipanggil, dengan meneruskan jenis data untuk pengelola.
Beberapa handler dapat didaftarkan dalam urutan yang harus mereka jalankan. Setiap handler membungkus handler berikutnya hingga final HttpClientHandler menjalankan permintaan:
services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();
services.AddHttpClient("clientwithhandlers")
// This handler is on the outside and called first during the
// request, last during the response.
.AddHttpMessageHandler<SecureRequestHandler>()
// This handler is on the inside, closest to the request being
// sent.
.AddHttpMessageHandler<RequestDataHandler>();
Menggunakan DI dalam middleware permintaan keluar
Saat IHttpClientFactory membuat handler pendelegasian baru, ia menggunakan DI untuk memenuhi parameter konstruktor handler.
IHttpClientFactory
membuat cakupan DI terpisah untuk setiap handler, yang dapat menyebabkan perilaku mengejutkan ketika handler mengonsumsi layanan tercakup.
Misalnya, pertimbangkan antarmuka berikut dan implementasinya, yang mewakili tugas sebagai operasi dengan pengidentifikasi, OperationId:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Seperti namanya, IOperationScoped terdaftar di DI menggunakan masa pakai tercakup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(options =>
options.UseInMemoryDatabase("TodoItems"));
services.AddHttpContextAccessor();
services.AddHttpClient<TodoClient>((sp, httpClient) =>
{
var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;
// For sample purposes, assume TodoClient is used in the context of an incoming request.
httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
httpRequest.Host, httpRequest.PathBase));
httpClient.Timeout = TimeSpan.FromSeconds(5);
});
services.AddScoped<IOperationScoped, OperationScoped>();
services.AddTransient<OperationHandler>();
services.AddTransient<OperationResponseHandler>();
services.AddHttpClient("Operation")
.AddHttpMessageHandler<OperationHandler>()
.AddHttpMessageHandler<OperationResponseHandler>()
.SetHandlerLifetime(TimeSpan.FromSeconds(5));
services.AddControllers();
services.AddRazorPages();
}
Penangan delegasi IOperationScoped berikut menggunakan IOperationScoped untuk mengatur tajuk untuk permintaan keluar.
public class OperationHandler : DelegatingHandler
{
private readonly IOperationScoped _operationService;
public OperationHandler(IOperationScoped operationScoped)
{
_operationService = operationScoped;
}
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-OPERATION-ID", _operationService.OperationId);
return await base.SendAsync(request, cancellationToken);
}
}
HttpRequestsSample Pada unduhan], navigasikan ke /Operation dan segarkan halaman. Nilai cakupan permintaan berubah untuk setiap permintaan, tetapi nilai cakupan handler hanya berubah setiap 5 detik.
Handler dapat bergantung pada layanan dari ruang lingkup mana pun. Layanan yang bergantung pada handler dibersihkan ketika handler dibersihkan.
Gunakan salah satu pendekatan berikut untuk berbagi status per permintaan dengan penangan pesan:
- Teruskan data ke handler menggunakan HttpRequestMessage.Options.
- Gunakan IHttpContextAccessor untuk mengakses permintaan saat ini.
- Buat objek penyimpanan kustom AsyncLocal<T> untuk meneruskan data.
Menggunakan handler berbasis Polly
IHttpClientFactory terintegrasi dengan pustaka pihak ketiga Polly. Polly adalah pustaka komprehensif untuk penanganan kesalahan sementara dan ketahanan pada .NET. Ini memungkinkan pengembang untuk mengekspresikan kebijakan seperti Retry, Circuit Breaker, Timeout, Bulkhead Isolation, dan Fallback dengan cara yang lancar dan aman utas.
Metode ekstensi disediakan untuk memungkinkan penerapan kebijakan Polly pada instans HttpClient yang telah dikonfigurasi. Ekstensi Polly mendukung penambahan handler berbasis Polly ke klien. Polly membutuhkan Microsoft. Extensions.Http.Polly paket NuGet.
Menangani kesalahan sementara
Kesalahan biasanya terjadi ketika panggilan HTTP eksternal bersifat sementara.
AddTransientHttpErrorPolicy memungkinkan kebijakan didefinisikan untuk menangani kesalahan sementara. Kebijakan yang dikonfigurasi dengan AddTransientHttpErrorPolicy menangani respons berikut:
- HttpRequestException
- HTTP 5xx
- HTTP 408
AddTransientHttpErrorPolicy menyediakan akses ke objek yang dikonfigurasi PolicyBuilder untuk menangani kesalahan yang mewakili kemungkinan kesalahan sementara:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
// Remaining code deleted for brevity.
Dalam kode sebelumnya, suatu kebijakan WaitAndRetryAsync ditentukan. Permintaan yang gagal dicoba ulang hingga tiga kali dengan penundaan 600 ms antar upaya.
Memilih kebijakan secara dinamis
Metode ekstensi disediakan untuk menambahkan handler berbasis Polly, misalnya, AddPolicyHandler. Kelebihan beban berikut AddPolicyHandler memeriksa permintaan untuk memutuskan kebijakan mana yang akan diterapkan:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
.AddPolicyHandler(request =>
request.Method == HttpMethod.Get ? timeout : longTimeout);
Dalam kode sebelumnya, jika permintaan keluar adalah HTTP GET, batas waktu 10 detik diterapkan. Untuk metode HTTP lainnya, batas waktu 30 detik digunakan.
Menambahkan beberapa handler Polly
Adalah umum untuk menyusun kebijakan Polly:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Dalam contoh sebelumnya:
- Dua handler ditambahkan.
- Handler pertama menggunakan AddTransientHttpErrorPolicy untuk menambahkan kebijakan coba ulang. Permintaan yang gagal dicoba ulang hingga tiga kali.
- Panggilan kedua
AddTransientHttpErrorPolicymenambahkan kebijakan pemutus sirkuit. Permintaan eksternal lebih lanjut diblokir selama 30 detik jika 5 upaya gagal terjadi secara berurutan. Kebijakan pemutus sirkuit bersifat stateful. Semua panggilan melalui klien ini memiliki status sirkuit yang sama.
Menambahkan kebijakan dari registri Polly
Pendekatan untuk mengelola kebijakan yang digunakan secara teratur adalah menentukannya sekali dan mendaftarkannya dengan PolicyRegistry.
Dalam kode berikut:
- Kebijakan "reguler" dan "panjang" ditambahkan.
- AddPolicyHandlerFromRegistry menambahkan kebijakan "reguler" dan "panjang" dari registri.
public void ConfigureServices(IServiceCollection services)
{
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regularTimeoutHandler")
.AddPolicyHandlerFromRegistry("regular");
services.AddHttpClient("longTimeoutHandler")
.AddPolicyHandlerFromRegistry("long");
// Remaining code deleted for brevity.
Untuk informasi selengkapnya tentang IHttpClientFactory dan integrasi Polly, lihat wiki Polly.
HttpClient dan manajemen siklus hidup
Instans HttpClient baru dikembalikan setiap kali CreateClient dipanggil pada IHttpClientFactory.
HttpMessageHandler Dibuat untuk setiap klien yang diberi nama. Pabrik mengelola masa pakai instans HttpMessageHandler.
IHttpClientFactory mengumpulkan instans HttpMessageHandler yang dibuat oleh pabrik untuk mengurangi konsumsi sumber daya. Instans HttpMessageHandler dapat digunakan kembali dari pool saat membuat instans HttpClient baru jika masa pakainya belum kedaluwarsa.
Pengelompokan handler diinginkan karena setiap handler biasanya mengelola koneksi HTTP yang mendasari secara mandiri. Membuat lebih banyak handler daripada yang dibutuhkan dapat mengakibatkan penundaan koneksi. Beberapa handler juga menjaga koneksi tetap terbuka tanpa batas waktu, yang dapat mencegah handler bereaksi terhadap perubahan DNS (Domain Name System).
Masa pakai handler default adalah dua menit. Nilai default dapat diganti untuk setiap klien yang diberi nama.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
// Remaining code deleted for brevity.
instans HttpClient umumnya dapat diperlakukan sebagai objek .NET tidak memerlukan penghapusan. Proses pembuangan membatalkan permintaan keluar dan menjamin instans HttpClient yang diberikan tidak dapat digunakan setelah memanggil Dispose.
IHttpClientFactory melacak dan membuang sumber daya yang digunakan oleh instans HttpClient.
Menjaga satu instans HttpClient tetap hidup untuk durasi yang lama adalah pola umum yang digunakan sebelum adanya IHttpClientFactory. Pola ini menjadi tidak perlu setelah bermigrasi ke IHttpClientFactory.
Alternatif untuk IHttpClientFactory
Menggunakan IHttpClientFactory dalam aplikasi yang diaktifkan DI menghindari:
- Masalah kelelahan sumber daya karena pengumpulan instans
HttpMessageHandler. - Mengatasi masalah DNS yang kedaluwarsa dengan mereset
HttpMessageHandlerinstans secara berkala.
Ada cara alternatif untuk menyelesaikan masalah sebelumnya menggunakan instans SocketsHttpHandler yang berusia panjang.
- Buat instans
SocketsHttpHandlersaat aplikasi dimulai dan gunakan untuk masa pakai aplikasi. - Konfigurasikan PooledConnectionLifetime ke nilai yang sesuai berdasarkan waktu refresh DNS.
- Buat
HttpClientinstance menggunakannew HttpClient(handler, disposeHandler: false)sesuai kebutuhan.
Pendekatan sebelumnya menyelesaikan masalah manajemen sumber daya yang IHttpClientFactory diselesaikan dengan cara yang sama.
- Koneksi
SocketsHttpHandlerberbagi antar instansHttpClient. Berbagi ini mencegah kelelahan soket. - Siklus
SocketsHttpHandlerkoneksi sesuaiPooledConnectionLifetimeuntuk menghindari masalah DNS usang.
Cookies
Instans yang dikumpulkan HttpMessageHandler menghasilkan CookieContainer objek yang dibagikan. Berbagi objek yang tidak diantisipasi CookieContainer sering kali menghasilkan kode yang tidak benar. Untuk aplikasi yang memerlukan cookie, pertimbangkan:
- Menonaktifkan penanganan otomatis cookie
- Menghindari
IHttpClientFactory
Panggilan ConfigurePrimaryHttpMessageHandler untuk menonaktifkan penanganan otomatis cookie :
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Logging
Klien yang dibuat melalui IHttpClientFactory mencatat pesan log untuk semua permintaan. Aktifkan tingkat informasi yang sesuai dalam konfigurasi pengelogan untuk melihat pesan log default. Pengelogan tambahan, seperti pengelogan header permintaan, hanya disertakan pada tingkat pelacakan.
Kategori log yang digunakan untuk setiap klien menyertakan nama klien. Klien bernama MyNamedClient, misalnya, mencatat pesan dengan kategori "System.Net.Http.HttpClient.MyNamedClient. LogicalHandler". Pesan yang dibubuhi dengan LogicalHandler terjadi di luar pipeline penanganan permintaan. Pada saat permintaan, pesan dicatat sebelum penangan lain dalam jalur telah memprosesnya. Pada respons, pesan dicatat setelah handler alur lain menerima respons.
Pengelogan juga terjadi di dalam alur handler permintaan. Dalam contoh MyNamedClient, pesan tersebut dicatat dengan kategori log "System.Net.Http.Http.HttpClient.MyNamedClient. ClientHandler". Untuk permintaan, ini terjadi setelah semua handler lain berjalan dan segera sebelum permintaan dikirim. Pada respons, pengelogan ini mencakup status respons sebelum melewati kembali melalui alur handler.
Mengaktifkan pengelogan di luar dan di dalam alur memungkinkan inspeksi perubahan yang dilakukan oleh penangan alur lainnya. Ini dapat mencakup perubahan pada header permintaan atau ke kode status respons.
Menyertakan nama klien dalam kategori log memungkinkan pemfilteran log untuk klien bernama tertentu.
Mengonfigurasi HttpMessageHandler
Anda mungkin perlu mengontrol konfigurasi HttpMessageHandler bagian dalam yang digunakan oleh klien.
IHttpClientBuilder dikembalikan saat menambahkan klien bernama atau berjenis. Metode ekstensi ConfigurePrimaryHttpMessageHandler dapat digunakan untuk menentukan delegat. Delegasi dipakai untuk membuat dan mengonfigurasi primer HttpMessageHandler yang digunakan oleh klien tersebut:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
// Remaining code deleted for brevity.
Menggunakan IHttpClientFactory di aplikasi konsol
Di aplikasi konsol, tambahkan referensi paket berikut ke proyek:
Dalam contoh berikut:
- IHttpClientFactory terdaftar dalam wadah layanan Generic Host.
-
MyServicemembuat instans pabrik klien dari layanan, yang digunakan untuk membuatHttpClient.HttpClientdigunakan untuk mengambil halaman web. -
Mainmembuat cakupan untuk menjalankan metode layananGetPagedan menulis 500 karakter pertama konten halaman web ke konsol.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<IMyService, MyService>();
}).UseConsoleLifetime();
var host = builder.Build();
try
{
var myService = host.Services.GetRequiredService<IMyService>();
var pageContent = await myService.GetPage();
Console.WriteLine(pageContent.Substring(0, 500));
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
return 0;
}
public interface IMyService
{
Task<string> GetPage();
}
public class MyService : IMyService
{
private readonly IHttpClientFactory _clientFactory;
public MyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetPage()
{
// Content from BBC One: Dr. Who website (©BBC)
var request = new HttpRequestMessage(HttpMethod.Get,
"https://www.bbc.co.uk/programmes/b006q2x0");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return $"StatusCode: {response.StatusCode}";
}
}
}
}
Middleware penyebaran header
Penyebaran header adalah middleware ASP.NET Core untuk menyebarluaskan header HTTP dari permintaan masuk ke permintaan Klien HTTP keluar. Untuk menggunakan penyebaran header:
Referensikan Microsoft. Paket AspNetCore.HeaderPropagation.
Konfigurasikan middleware dan
HttpClientdiStartup:public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddHttpClient("MyForwardingClient").AddHeaderPropagation(); services.AddHeaderPropagation(options => { options.Headers.Add("X-TraceId"); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseHeaderPropagation(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }Klien menyertakan header yang dikonfigurasi pada permintaan keluar:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);
Sumber daya tambahan
Oleh Kirk Larkin, Steve Gordon, Glenn Condron, dan Ryan Nowak.
IHttpClientFactory dapat didaftarkan dan digunakan untuk mengonfigurasi dan membuat HttpClient instance di dalam aplikasi.
IHttpClientFactory menawarkan manfaat berikut:
- Menyediakan lokasi pusat untuk penamaan dan konfigurasi instans
HttpClientlogis. Misalnya, klien bernama github dapat didaftarkan dan dikonfigurasi untuk mengakses GitHub. Klien default dapat didaftarkan untuk akses umum. - Mengkodifikasi konsep middleware outbound melalui pendelegasian handler di
HttpClient. Menyediakan ekstensi untuk middleware berbasis Polly untuk memanfaatkan pendelegasian handler diHttpClient. - Mengelola pengumpulan dan masa pakai instans
HttpClientMessageHandleryang mendasarinya. Manajemen otomatis menghindari masalah DNS umum (Sistem Nama Domain) yang terjadi saat mengelolaHttpClientmasa pakai secara manual. - Menambahkan pengalaman pengelogan yang bisa dikonfigurasi (melalui
ILogger) untuk semua permintaan yang dikirim melalui klien yang dibuat oleh pabrik.
Lihat atau unduh sampel kode (cara mengunduh).
Kode sampel dalam versi topik ini menggunakan System.Text.Json untuk mendeserialisasi konten JSON yang dikembalikan dalam respons HTTP. Untuk sampel yang menggunakan Json.NET dan ReadAsAsync<T>, gunakan pemilih versi untuk memilih versi 2.x dari topik ini.
Pola konsumsi
Ada beberapa cara menggunakan IHttpClientFactory dalam aplikasi:
Pendekatan terbaiknya tergantung pada persyaratan aplikasi.
Penggunaan dasar
IHttpClientFactory dapat didaftarkan dengan memanggil AddHttpClient:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// Remaining code deleted for brevity.
Sebuah IHttpClientFactory dapat diminta menggunakan injeksi dependensi (DI). Kode berikut ini menggunakan IHttpClientFactory untuk membuat instansHttpClient:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubBranch> Branches { get; private set; }
public bool GetBranchesError { get; private set; }
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
Branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
GetBranchesError = true;
Branches = Array.Empty<GitHubBranch>();
}
}
}
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
HttpClientyang berbeda. - Banyak
HttpClientyang memiliki konfigurasi yang berbeda.
Konfigurasi untuk HttpClient yang bernama dapat ditentukan selama pendaftaran di Startup.ConfigureServices:
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Dalam kode sebelumnya, klien dikonfigurasi dengan:
- Alamat dasar
https://api.github.com/. - Dua header yang diperlukan untuk bekerja dengan API GitHub.
CreateClient
Setiap kali CreateClient disebut:
- Instans
HttpClientbaru dibuat. - Tindakan konfigurasi dijalankan.
Untuk membuat klien bernama, berikan namanya ke dalam CreateClient:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }
public bool GetPullRequestsError { get; private set; }
public bool HasPullRequests => PullRequests.Any();
public NamedClientModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"repos/dotnet/AspNetCore.Docs/pulls");
var client = _clientFactory.CreateClient("github");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
PullRequests = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubPullRequest>>(responseStream);
}
else
{
GetPullRequestsError = true;
PullRequests = Array.Empty<GitHubPullRequest>();
}
}
}
Dalam kode sebelumnya, permintaan tidak perlu menentukan nama host. Kode hanya bisa melewati jalur karena alamat dasar yang dikonfigurasi untuk klien digunakan.
Klien yang dititik
Klien yang dititik:
- Memberikan kemampuan yang sama seperti klien bernama tanpa perlu menggunakan string sebagai kunci.
- Menyediakan bantuan IntelliSense dan bantuan dari compiler saat menggunakan klien.
- Menyediakan satu lokasi untuk mengonfigurasi dan berinteraksi dengan
HttpClienttertentu. Misalnya, klien bertipe 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 bertipe menerima parameter HttpClient di dalam konstruktornya.
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await Client.GetAsync(
"/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubIssue>>(responseStream);
}
}
Dalam kode sebelumnya:
- Konfigurasi dipindahkan ke klien yang ditik.
- Objek
HttpClientdiekspos sebagaipublicproperti.
Metode khusus API bisa dibuat yang mengekspos fungsionalitas HttpClient. Misalnya, metode GetAspNetDocsIssues mengenkapsulasi kode untuk mengambil masalah terbuka.
Kode berikut ini memanggil AddHttpClient pada Startup.ConfigureServices untuk mendaftarkan kelas klien berjenis:
services.AddHttpClient<GitHubService>();
Klien bertipe didaftarkan sebagai transien di dalam DI. Dalam kode sebelumnya, AddHttpClient mendaftarkan GitHubService sebagai layanan sementara. Pendaftaran ini menggunakan metode pabrik untuk:
- Buat instans
HttpClient. - Membuat instans
GitHubService, meneruskan instansHttpClientke konstruktornya.
Klien terketik dapat langsung disuntikkan dan digunakan.
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public IEnumerable<GitHubIssue> LatestIssues { get; private set; }
public bool HasIssue => LatestIssues.Any();
public bool GetIssuesError { get; private set; }
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
catch(HttpRequestException)
{
GetIssuesError = true;
LatestIssues = Array.Empty<GitHubIssue>();
}
}
}
Konfigurasi untuk klien yang ditik dapat ditentukan selama pendaftaran di Startup.ConfigureServices, bukan di konstruktor klien yang ditik:
services.AddHttpClient<RepoService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
HttpClient dapat dienkapsulasi dalam klien yang ditik. Daripada mengeksposnya sebagai properti, tentukan metode yang memanggil instans HttpClient secara internal:
public class RepoService
{
// _httpClient isn't exposed publicly
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();
return await JsonSerializer.DeserializeAsync
<IEnumerable<string>>(responseStream);
}
}
Dalam kode sebelumnya, HttpClient disimpan di bidang privat. Akses ke HttpClient adalah dengan metode publik GetRepos .
Klien yang dihasilkan
IHttpClientFactory bisa digunakan dalam kombinasi dengan pustaka pihak ketiga seperti Refit. Refit adalah pustaka REST untuk .NET. Ini mengonversi REST API menjadi antarmuka langsung. Implementasi antarmuka dihasilkan secara dinamis oleh RestService, memakai HttpClient untuk melakukan panggilan HTTP eksternal.
Antarmuka dan balasan didefinisikan untuk mewakili API eksternal dan responsnya:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
"Klien dengan tipe dapat ditambahkan, menggunakan Refit untuk membuat implementasi:"
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddControllers();
}
Antarmuka yang ditentukan dapat dikonsumsi jika perlu, dengan implementasi yang disediakan oleh DI dan Refit:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
Membuat permintaan POST, PUT, dan DELETE
Dalam 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.
Contoh berikut menunjukkan cara membuat permintaan HTTP POST:
public async Task CreateItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem, _jsonSerializerOptions),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PostAsync("/api/TodoItems", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Dalam kode sebelumnya, metode CreateItemAsync:
- Menserialisasikan parameter
TodoItemke JSON menggunakanSystem.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 menunjukkan permintaan HTTP PUT:
public async Task SaveItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Kode sebelumnya sangat mirip dengan contoh POST. Metode SaveItemAsync memanggil PutAsync alih-alih PostAsync.
Contoh berikut menunjukkan permintaan HTTP DELETE:
public async Task DeleteItemAsync(long itemId)
{
using var httpResponse =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
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.
Middleware untuk permintaan yang keluar
HttpClient memiliki konsep mendelegasikan handler yang dapat ditautkan bersama untuk permintaan HTTP keluar.
IHttpClientFactory:
- Menyederhanakan penentuan handler yang diterapkan pada setiap klien dengan nama tertentu.
- Mendukung pendaftaran dan penautan beberapa handler untuk membangun alur middleware permintaan keluar. Masing-masing handler ini dapat melakukan tugas sebelum dan sesudah permintaan keluar. Pola ini:
- Mirip dengan alur middleware masuk di ASP.NET Core.
- Menyediakan mekanisme untuk mengelola concern lintas dalam permintaan HTTP, seperti:
- penembolokan
- penanganan kesalahan
- serialization
- pengelogan
Untuk membuat pengendali pendelegasian:
- Berasal dari DelegatingHandler.
- Ambil alih SendAsync. Jalankan kode sebelum meneruskan permintaan ke handler berikutnya dalam alur:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Kode sebelumnya memeriksa apakah X-API-KEY header ada dalam permintaan. Jika X-API-KEY hilang, BadRequest dikembalikan.
Lebih dari satu handler dapat ditambahkan ke konfigurasi untuk HttpClient dengan Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
// Remaining code deleted for brevity.
Dalam kode sebelumnya, ValidateHeaderHandler terdaftar di DI. Setelah terdaftar, AddHttpMessageHandler dapat dipanggil, dengan meneruskan jenis data untuk pengelola.
Beberapa handler dapat didaftarkan dalam urutan yang harus mereka jalankan. Setiap handler membungkus handler berikutnya hingga final HttpClientHandler menjalankan permintaan:
services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();
services.AddHttpClient("clientwithhandlers")
// This handler is on the outside and called first during the
// request, last during the response.
.AddHttpMessageHandler<SecureRequestHandler>()
// This handler is on the inside, closest to the request being
// sent.
.AddHttpMessageHandler<RequestDataHandler>();
Menggunakan DI dalam middleware permintaan keluar
Saat IHttpClientFactory membuat handler pendelegasian baru, ia menggunakan DI untuk memenuhi parameter konstruktor handler.
IHttpClientFactory
membuat cakupan DI terpisah untuk setiap handler, yang dapat menyebabkan perilaku mengejutkan ketika handler mengonsumsi layanan tercakup.
Misalnya, pertimbangkan antarmuka berikut dan implementasinya, yang mewakili tugas sebagai operasi dengan pengidentifikasi, OperationId:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Seperti namanya, IOperationScoped terdaftar di DI menggunakan masa pakai tercakup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(options =>
options.UseInMemoryDatabase("TodoItems"));
services.AddHttpContextAccessor();
services.AddHttpClient<TodoClient>((sp, httpClient) =>
{
var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;
// For sample purposes, assume TodoClient is used in the context of an incoming request.
httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
httpRequest.Host, httpRequest.PathBase));
httpClient.Timeout = TimeSpan.FromSeconds(5);
});
services.AddScoped<IOperationScoped, OperationScoped>();
services.AddTransient<OperationHandler>();
services.AddTransient<OperationResponseHandler>();
services.AddHttpClient("Operation")
.AddHttpMessageHandler<OperationHandler>()
.AddHttpMessageHandler<OperationResponseHandler>()
.SetHandlerLifetime(TimeSpan.FromSeconds(5));
services.AddControllers();
services.AddRazorPages();
}
Penangan delegasi IOperationScoped berikut menggunakan IOperationScoped untuk mengatur tajuk untuk permintaan keluar.
public class OperationHandler : DelegatingHandler
{
private readonly IOperationScoped _operationService;
public OperationHandler(IOperationScoped operationScoped)
{
_operationService = operationScoped;
}
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-OPERATION-ID", _operationService.OperationId);
return await base.SendAsync(request, cancellationToken);
}
}
HttpRequestsSample Pada unduhan], navigasikan ke /Operation dan segarkan halaman. Nilai cakupan permintaan berubah untuk setiap permintaan, tetapi nilai cakupan handler hanya berubah setiap 5 detik.
Handler dapat bergantung pada layanan dari ruang lingkup mana pun. Layanan yang bergantung pada handler dibersihkan ketika handler dibersihkan.
Gunakan salah satu pendekatan berikut untuk berbagi status per permintaan dengan penangan pesan:
- Teruskan data ke handler menggunakan HttpRequestMessage.Properties.
- Gunakan IHttpContextAccessor untuk mengakses permintaan saat ini.
- Buat objek penyimpanan kustom AsyncLocal<T> untuk meneruskan data.
Menggunakan handler berbasis Polly
IHttpClientFactory terintegrasi dengan pustaka pihak ketiga Polly. Polly adalah pustaka komprehensif untuk ketahanan dan penanganan kesalahan sementara di .NET. Ini memungkinkan pengembang untuk mengekspresikan kebijakan seperti Retry, Circuit Breaker, Timeout, Bulkhead Isolation, dan Fallback dengan cara yang lancar dan aman utas.
Metode ekstensi disediakan untuk memungkinkan penerapan kebijakan Polly pada instans HttpClient yang telah dikonfigurasi. Ekstensi Polly mendukung penambahan handler berbasis Polly ke klien. Polly membutuhkan Microsoft. Extensions.Http.Polly paket NuGet.
Menangani kesalahan sementara
Kesalahan biasanya terjadi ketika panggilan HTTP eksternal bersifat sementara.
AddTransientHttpErrorPolicy memungkinkan kebijakan didefinisikan untuk menangani kesalahan sementara. Kebijakan yang dikonfigurasi dengan AddTransientHttpErrorPolicy menangani respons berikut:
- HttpRequestException
- HTTP 5xx
- HTTP 408
AddTransientHttpErrorPolicy menyediakan akses ke objek yang dikonfigurasi PolicyBuilder untuk menangani kesalahan yang mewakili kemungkinan kesalahan sementara:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
// Remaining code deleted for brevity.
Dalam kode sebelumnya, suatu kebijakan WaitAndRetryAsync ditentukan. Permintaan yang gagal dicoba ulang hingga tiga kali dengan penundaan 600 ms antar upaya.
Memilih kebijakan secara dinamis
Metode ekstensi disediakan untuk menambahkan handler berbasis Polly, misalnya, AddPolicyHandler. Kelebihan beban berikut AddPolicyHandler memeriksa permintaan untuk memutuskan kebijakan mana yang akan diterapkan:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
.AddPolicyHandler(request =>
request.Method == HttpMethod.Get ? timeout : longTimeout);
Dalam kode sebelumnya, jika permintaan keluar adalah HTTP GET, batas waktu 10 detik diterapkan. Untuk metode HTTP lainnya, batas waktu 30 detik digunakan.
Menambahkan beberapa handler Polly
Adalah umum untuk menyusun kebijakan Polly:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Dalam contoh sebelumnya:
- Dua handler ditambahkan.
- Handler pertama menggunakan AddTransientHttpErrorPolicy untuk menambahkan kebijakan coba ulang. Permintaan yang gagal dicoba ulang hingga tiga kali.
- Panggilan kedua
AddTransientHttpErrorPolicymenambahkan kebijakan pemutus sirkuit. Permintaan eksternal lebih lanjut diblokir selama 30 detik jika 5 upaya gagal terjadi secara berurutan. Kebijakan pemutus sirkuit bersifat stateful. Semua panggilan melalui klien ini memiliki status sirkuit yang sama.
Menambahkan kebijakan dari registri Polly
Pendekatan untuk mengelola kebijakan yang digunakan secara teratur adalah menentukannya sekali dan mendaftarkannya dengan PolicyRegistry.
Dalam kode berikut:
- Kebijakan "reguler" dan "panjang" ditambahkan.
- AddPolicyHandlerFromRegistry menambahkan kebijakan "reguler" dan "panjang" dari registri.
public void ConfigureServices(IServiceCollection services)
{
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regularTimeoutHandler")
.AddPolicyHandlerFromRegistry("regular");
services.AddHttpClient("longTimeoutHandler")
.AddPolicyHandlerFromRegistry("long");
// Remaining code deleted for brevity.
Untuk informasi selengkapnya tentang IHttpClientFactory dan integrasi Polly, lihat wiki Polly.
HttpClient dan manajemen siklus hidup
Instans HttpClient baru dikembalikan setiap kali CreateClient dipanggil pada IHttpClientFactory.
HttpMessageHandler Dibuat untuk setiap klien yang diberi nama. Pabrik mengelola masa pakai instans HttpMessageHandler.
IHttpClientFactory mengumpulkan instans HttpMessageHandler yang dibuat oleh pabrik untuk mengurangi konsumsi sumber daya. Instans HttpMessageHandler dapat digunakan kembali dari pool saat membuat instans HttpClient baru jika masa pakainya belum kedaluwarsa.
Pengelompokan handler diinginkan karena setiap handler biasanya mengelola koneksi HTTP yang mendasari secara mandiri. Membuat lebih banyak handler daripada yang dibutuhkan dapat mengakibatkan penundaan koneksi. Beberapa handler juga menjaga koneksi tetap terbuka tanpa batas waktu, yang dapat mencegah handler bereaksi terhadap perubahan DNS (Domain Name System).
Masa pakai handler default adalah dua menit. Nilai default dapat diganti untuk setiap klien yang diberi nama.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
// Remaining code deleted for brevity.
instans HttpClient umumnya dapat diperlakukan sebagai objek .NET tidak memerlukan penghapusan. Proses pembuangan membatalkan permintaan keluar dan menjamin instans HttpClient yang diberikan tidak dapat digunakan setelah memanggil Dispose.
IHttpClientFactory melacak dan membuang sumber daya yang digunakan oleh instans HttpClient.
Menjaga satu instans HttpClient tetap hidup untuk durasi yang lama adalah pola umum yang digunakan sebelum adanya IHttpClientFactory. Pola ini menjadi tidak perlu setelah bermigrasi ke IHttpClientFactory.
Alternatif untuk IHttpClientFactory
Menggunakan IHttpClientFactory dalam aplikasi yang diaktifkan DI menghindari:
- Masalah kelelahan sumber daya karena pengumpulan instans
HttpMessageHandler. - Mengatasi masalah DNS yang kedaluwarsa dengan mereset
HttpMessageHandlerinstans secara berkala.
Ada cara alternatif untuk menyelesaikan masalah sebelumnya menggunakan instans SocketsHttpHandler yang berusia panjang.
- Buat instans
SocketsHttpHandlersaat aplikasi dimulai dan gunakan untuk masa pakai aplikasi. - Konfigurasikan PooledConnectionLifetime ke nilai yang sesuai berdasarkan waktu refresh DNS.
- Buat
HttpClientinstance menggunakannew HttpClient(handler, disposeHandler: false)sesuai kebutuhan.
Pendekatan sebelumnya menyelesaikan masalah manajemen sumber daya yang IHttpClientFactory diselesaikan dengan cara yang sama.
- Koneksi
SocketsHttpHandlerberbagi antar instansHttpClient. Berbagi ini mencegah kelelahan soket. - Siklus
SocketsHttpHandlerkoneksi sesuaiPooledConnectionLifetimeuntuk menghindari masalah DNS usang.
Cookies
Instans yang dikumpulkan HttpMessageHandler menghasilkan CookieContainer objek yang dibagikan. Berbagi objek yang tidak diantisipasi CookieContainer sering kali menghasilkan kode yang tidak benar. Untuk aplikasi yang memerlukan cookie, pertimbangkan:
- Menonaktifkan penanganan otomatis cookie
- Menghindari
IHttpClientFactory
Panggilan ConfigurePrimaryHttpMessageHandler untuk menonaktifkan penanganan otomatis cookie :
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Logging
Klien yang dibuat melalui IHttpClientFactory mencatat pesan log untuk semua permintaan. Aktifkan tingkat informasi yang sesuai dalam konfigurasi pengelogan untuk melihat pesan log default. Pengelogan tambahan, seperti pengelogan header permintaan, hanya disertakan pada tingkat pelacakan.
Kategori log yang digunakan untuk setiap klien menyertakan nama klien. Klien bernama MyNamedClient, misalnya, mencatat pesan dengan kategori "System.Net.Http.HttpClient.MyNamedClient. LogicalHandler". Pesan yang dibubuhi dengan LogicalHandler terjadi di luar pipeline penanganan permintaan. Pada saat permintaan, pesan dicatat sebelum penangan lain dalam jalur telah memprosesnya. Pada respons, pesan dicatat setelah handler alur lain menerima respons.
Pengelogan juga terjadi di dalam alur handler permintaan. Dalam contoh MyNamedClient, pesan tersebut dicatat dengan kategori log "System.Net.Http.Http.HttpClient.MyNamedClient. ClientHandler". Untuk permintaan, ini terjadi setelah semua handler lain berjalan dan segera sebelum permintaan dikirim. Pada respons, pengelogan ini mencakup status respons sebelum melewati kembali melalui alur handler.
Mengaktifkan pengelogan di luar dan di dalam alur memungkinkan inspeksi perubahan yang dilakukan oleh penangan alur lainnya. Ini dapat mencakup perubahan pada header permintaan atau ke kode status respons.
Menyertakan nama klien dalam kategori log memungkinkan pemfilteran log untuk klien bernama tertentu.
Mengonfigurasi HttpMessageHandler
Anda mungkin perlu mengontrol konfigurasi HttpMessageHandler bagian dalam yang digunakan oleh klien.
IHttpClientBuilder dikembalikan saat menambahkan klien bernama atau berjenis. Metode ekstensi ConfigurePrimaryHttpMessageHandler dapat digunakan untuk menentukan delegat. Delegasi dipakai untuk membuat dan mengonfigurasi primer HttpMessageHandler yang digunakan oleh klien tersebut:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
// Remaining code deleted for brevity.
Menggunakan IHttpClientFactory di aplikasi konsol
Di aplikasi konsol, tambahkan referensi paket berikut ke proyek:
Dalam contoh berikut:
- IHttpClientFactory terdaftar dalam wadah layanan Generic Host.
-
MyServicemembuat instans pabrik klien dari layanan, yang digunakan untuk membuatHttpClient.HttpClientdigunakan untuk mengambil halaman web. -
Mainmembuat cakupan untuk menjalankan metode layananGetPagedan menulis 500 karakter pertama konten halaman web ke konsol.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<IMyService, MyService>();
}).UseConsoleLifetime();
var host = builder.Build();
try
{
var myService = host.Services.GetRequiredService<IMyService>();
var pageContent = await myService.GetPage();
Console.WriteLine(pageContent.Substring(0, 500));
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
return 0;
}
public interface IMyService
{
Task<string> GetPage();
}
public class MyService : IMyService
{
private readonly IHttpClientFactory _clientFactory;
public MyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetPage()
{
// Content from BBC One: Dr. Who website (©BBC)
var request = new HttpRequestMessage(HttpMethod.Get,
"https://www.bbc.co.uk/programmes/b006q2x0");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return $"StatusCode: {response.StatusCode}";
}
}
}
}
Middleware penyebaran header
Penyebaran header adalah middleware ASP.NET Core untuk menyebarluaskan header HTTP dari permintaan masuk ke permintaan Klien HTTP keluar. Untuk menggunakan penyebaran header:
Referensikan Microsoft. Paket AspNetCore.HeaderPropagation.
Konfigurasikan middleware dan
HttpClientdiStartup:public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddHttpClient("MyForwardingClient").AddHeaderPropagation(); services.AddHeaderPropagation(options => { options.Headers.Add("X-TraceId"); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseHeaderPropagation(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }Klien menyertakan header yang dikonfigurasi pada permintaan keluar:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);
Sumber daya tambahan
Oleh Glenn Condron, Ryan Nowak, dan Steve Gordon
IHttpClientFactory dapat didaftarkan dan digunakan untuk mengonfigurasi dan membuat HttpClient instance di dalam aplikasi. Ini menawarkan manfaat berikut:
- Menyediakan lokasi pusat untuk penamaan dan konfigurasi instans
HttpClientlogis. Misalnya, klien github dapat didaftarkan dan dikonfigurasi untuk mengakses GitHub. Klien default dapat didaftarkan untuk tujuan lain. - Mengkodifikasi konsep middleware keluar melalui pendelegasian handler di
HttpClientdan menyediakan ekstensi untuk memanfaatkan middleware berbasis Polly. - Mengelola pengumpulan dan masa pakai
HttpClientMessageHandlerinstans yang mendasarinya untuk menghindari masalah DNS umum yang terjadi saat mengelola masa pakaiHttpClientsecara manual. - Menambahkan pengalaman pengelogan yang bisa dikonfigurasi (melalui
ILogger) untuk semua permintaan yang dikirim melalui klien yang dibuat oleh pabrik.
Melihat atau mengunduh kode sampel (cara mengunduh)
Prerequisites
Proyek yang menargetkan .NET Framework memerlukan penginstalan Microsoft. Extensions.Http paket NuGet. Proyek yang menargetkan .NET Core dan mengacu pada metapackage Microsoft.AspNetCore.App sudah menyertakan paket Microsoft.Extensions.Http.
Pola konsumsi
Ada beberapa cara menggunakan IHttpClientFactory dalam aplikasi:
Tidak satu pun dari mereka benar-benar lebih unggul daripada yang lain. Pendekatan terbaik tergantung pada batasan aplikasi.
Penggunaan dasar
IHttpClientFactory dapat didaftarkan dengan memanggil metode ekstensi AddHttpClient pada IServiceCollection, di dalam metode Startup.ConfigureServices.
services.AddHttpClient();
Setelah terdaftar, kode dapat menerima IHttpClientFactory di mana saja injeksi dependensi (DI) dapat dilakukan.
IHttpClientFactory dapat digunakan untuk membuat HttpClient instance:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubBranch> Branches { get; private set; }
public bool GetBranchesError { get; private set; }
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Branches = await response.Content
.ReadAsAsync<IEnumerable<GitHubBranch>>();
}
else
{
GetBranchesError = true;
Branches = Array.Empty<GitHubBranch>();
}
}
}
Menggunakan IHttpClientFactory dengan cara ini adalah cara yang baik untuk merefaktor aplikasi yang ada. Ini tidak berdampak pada cara HttpClient digunakan. Di tempat-tempat di mana HttpClient instans saat ini dibuat, ganti kemunculan tersebut dengan memanggil CreateClient.
Klien bernama
Jika aplikasi memerlukan banyak penggunaan yang berbeda dari HttpClient, masing-masing dengan konfigurasi yang berbeda, opsinya adalah menggunakan klien bernama. Konfigurasi untuk nama HttpClient dapat ditentukan selama pendaftaran di Startup.ConfigureServices.
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Dalam kode sebelumnya, AddHttpClient dipanggil, memberikan nama github. Klien ini memiliki beberapa konfigurasi default yang diterapkan—yaitu alamat dasar dan dua header yang diperlukan untuk bekerja dengan API GitHub.
Setiap kali CreateClient dipanggil, instans HttpClient baru dibuat dan tindakan konfigurasi dipanggil.
Untuk menggunakan klien bernama, parameter string dapat diteruskan ke CreateClient. Tentukan nama klien yang akan dibuat:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }
public bool GetPullRequestsError { get; private set; }
public bool HasPullRequests => PullRequests.Any();
public NamedClientModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"repos/dotnet/AspNetCore.Docs/pulls");
var client = _clientFactory.CreateClient("github");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
PullRequests = await response.Content
.ReadAsAsync<IEnumerable<GitHubPullRequest>>();
}
else
{
GetPullRequestsError = true;
PullRequests = Array.Empty<GitHubPullRequest>();
}
}
}
Dalam kode sebelumnya, permintaan tidak perlu menentukan nama host. Ini hanya dapat melewati jalur, karena alamat dasar yang dikonfigurasi untuk klien digunakan.
Klien yang dititik
Klien yang dititik:
- Memberikan kemampuan yang sama seperti klien bernama tanpa perlu menggunakan string sebagai kunci.
- Menyediakan bantuan IntelliSense dan bantuan dari compiler saat menggunakan klien.
- Menyediakan satu lokasi untuk mengonfigurasi dan berinteraksi dengan
HttpClienttertentu. Misalnya, klien bertipe tunggal mungkin digunakan untuk satu titik akhir backend dan mengenkapsulasi semua logika yang berhubungan dengan titik akhir tersebut. - Bekerja dengan DI dan dapat disuntikkan jika diperlukan di aplikasi Anda.
Klien bertipe menerima parameter HttpClient di dalam konstruktornya.
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await Client.GetAsync(
"/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
var result = await response.Content
.ReadAsAsync<IEnumerable<GitHubIssue>>();
return result;
}
}
Dalam kode sebelumnya, konfigurasi dipindahkan ke klien yang ditik. Objek HttpClient diekspos sebagai public properti. Dimungkinkan untuk mendefinisikan metode khusus API yang mengekspos fungsionalitas HttpClient. Metode GetAspNetDocsIssues merangkum kode yang diperlukan untuk mengkueri dan mengurai masalah terbuka terbaru dari repositori GitHub.
Untuk mendaftarkan klien yang ditik, metode ekstensi generik AddHttpClient dapat digunakan dalam Startup.ConfigureServices, menentukan kelas klien yang ditik:
services.AddHttpClient<GitHubService>();
Klien bertipe didaftarkan sebagai transien di dalam DI. Klien terketik dapat langsung disuntikkan dan digunakan.
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public IEnumerable<GitHubIssue> LatestIssues { get; private set; }
public bool HasIssue => LatestIssues.Any();
public bool GetIssuesError { get; private set; }
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
catch(HttpRequestException)
{
GetIssuesError = true;
LatestIssues = Array.Empty<GitHubIssue>();
}
}
}
Jika lebih disukai, konfigurasi untuk klien bertipe dapat ditentukan selama registrasi di Startup.ConfigureServices, daripada di konstruktor klien bertipe.
services.AddHttpClient<RepoService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Dimungkinkan untuk sepenuhnya merangkum HttpClient dalam klien yang ditik. Daripada diekspos sebagai properti, metode publik dapat disediakan untuk memanggil instans HttpClient secara internal.
public class RepoService
{
// _httpClient isn't exposed publicly
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();
var result = await response.Content
.ReadAsAsync<IEnumerable<string>>();
return result;
}
}
Dalam kode sebelumnya, HttpClient disimpan sebagai bidang privat. Semua akses untuk melakukan panggilan eksternal melewati metode GetRepos.
Klien yang dihasilkan
IHttpClientFactory dapat digunakan dalam kombinasi dengan pustaka pihak ketiga lainnya seperti Refit. Refit adalah pustaka REST untuk .NET. Ini mengonversi REST API menjadi antarmuka langsung. Implementasi antarmuka dihasilkan secara dinamis oleh RestService, memakai HttpClient untuk melakukan panggilan HTTP eksternal.
Antarmuka dan balasan didefinisikan untuk mewakili API eksternal dan responsnya:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
"Klien dengan tipe dapat ditambahkan, menggunakan Refit untuk membuat implementasi:"
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddMvc();
}
Antarmuka yang ditentukan dapat dikonsumsi jika perlu, dengan implementasi yang disediakan oleh DI dan Refit:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
Middleware untuk permintaan yang keluar
HttpClient sudah memiliki konsep mendelegasikan handler yang dapat ditautkan bersama untuk permintaan HTTP keluar.
IHttpClientFactory membuatnya mudah untuk menentukan handler yang diterapkan pada setiap klien yang ditentukan nama-namanya. Ini mendukung pendaftaran dan penautan beberapa handler untuk membangun alur middleware permintaan keluar. Masing-masing handler ini dapat melakukan tugas sebelum dan sesudah permintaan keluar. Pola ini mirip dengan alur middleware masuk di ASP.NET Core. Pola ini menyediakan mekanisme untuk mengelola masalah lintas fungsi seputar permintaan HTTP, termasuk penyimpanan sementara, penanganan kesalahan, serialisasi, dan pencatatan.
Untuk membuat handler, tentukan kelas yang berasal dari DelegatingHandler. Ambil alih metode SendAsync untuk menjalankan kode sebelum meneruskan permintaan ke handler berikutnya di pipeline.
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Kode sebelumnya mendefinisikan handler dasar. Ini memeriksa apakah X-API-KEY header telah disertakan pada permintaan. Jika header hilang, panggilan HTTP dapat dihindari dan memberikan respons yang sesuai.
Selama pendaftaran, satu atau beberapa handler dapat ditambahkan ke konfigurasi untuk HttpClient. Tugas ini dicapai melalui metode ekstensi pada IHttpClientBuilder.
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5000/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
Dalam kode sebelumnya, ValidateHeaderHandler terdaftar di DI. Handler harus terdaftar di DI sebagai layanan sementara, tidak pernah tercakup. Jika handler terdaftar sebagai layanan tercakup dan layanan apa pun yang diandalkan handler dapat digunakan:
- Layanan handler dapat dihentikan sebelum handler tidak lagi berada dalam cakupan.
- Layanan terkait handler yang telah dibuang menyebabkan handler mengalami kegagalan.
Setelah terdaftar, AddHttpMessageHandler dapat dipanggil, dengan memasukkan jenis handler.
Beberapa handler dapat didaftarkan dalam urutan yang harus mereka jalankan. Setiap handler membungkus handler berikutnya hingga final HttpClientHandler menjalankan permintaan:
services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();
services.AddHttpClient("clientwithhandlers")
// This handler is on the outside and called first during the
// request, last during the response.
.AddHttpMessageHandler<SecureRequestHandler>()
// This handler is on the inside, closest to the request being
// sent.
.AddHttpMessageHandler<RequestDataHandler>();
Gunakan salah satu pendekatan berikut untuk berbagi status per permintaan dengan penangan pesan:
- Teruskan data ke handler menggunakan
HttpRequestMessage.Properties. - Gunakan
IHttpContextAccessoruntuk mengakses permintaan saat ini. - Buat objek penyimpanan kustom
AsyncLocaluntuk meneruskan data.
Menggunakan handler berbasis Polly
IHttpClientFactory terintegrasi dengan pustaka pihak ketiga populer yang disebut Polly. Polly adalah pustaka komprehensif untuk penanganan kesalahan sementara dan ketahanan pada .NET. Ini memungkinkan pengembang untuk mengekspresikan kebijakan seperti Retry, Circuit Breaker, Timeout, Bulkhead Isolation, dan Fallback dengan cara yang lancar dan aman utas.
Metode ekstensi disediakan untuk memungkinkan penerapan kebijakan Polly pada instans HttpClient yang telah dikonfigurasi. Ekstensi Polly:
- Mendukung penambahan handler berbasis Polly ke klien.
- Dapat digunakan setelah menginstal Microsoft. Extensions.Http.Polly paket NuGet. Paket tidak disertakan dalam kerangka kerja bersama ASP.NET Core.
Menangani kesalahan sementara
Kesalahan paling umum terjadi ketika panggilan HTTP eksternal bersifat sementara. Metode ekstensi mudah yang disebut AddTransientHttpErrorPolicy disertakan yang memungkinkan kebijakan didefinisikan untuk menangani kesalahan sementara. Kebijakan yang dikonfigurasi dengan metode ekstensi ini menangani HttpRequestException, respons HTTP 5xx, dan respons HTTP 408.
AddTransientHttpErrorPolicy Ekstensi dapat digunakan dalam Startup.ConfigureServices. Ekstensi ini menyediakan akses ke objek yang dikonfigurasi PolicyBuilder untuk menangani kesalahan yang mewakili kemungkinan kesalahan sementara:
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
Dalam kode sebelumnya, suatu kebijakan WaitAndRetryAsync ditentukan. Permintaan yang gagal dicoba ulang hingga tiga kali dengan penundaan 600 ms antar upaya.
Memilih kebijakan secara dinamis
Ada metode ekstensi tambahan yang dapat digunakan untuk menambahkan handler berbasis Polly. Salah satu ekstensi tersebut adalah AddPolicyHandler, yang memiliki beberapa kelebihan beban. Satu kelebihan beban memungkinkan permintaan diperiksa saat menentukan kebijakan mana yang akan diterapkan:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
.AddPolicyHandler(request =>
request.Method == HttpMethod.Get ? timeout : longTimeout);
Dalam kode sebelumnya, jika permintaan keluar adalah HTTP GET, batas waktu 10 detik diterapkan. Untuk metode HTTP lainnya, batas waktu 30 detik digunakan.
Menambahkan beberapa handler Polly
Merupakan hal yang umum untuk menggabungkan kebijakan Polly untuk menyediakan fungsionalitas yang lebih baik.
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Dalam contoh sebelumnya, dua handler ditambahkan. Yang pertama menggunakan AddTransientHttpErrorPolicy ekstensi untuk menambahkan kebijakan coba lagi. Permintaan yang gagal dicoba ulang hingga tiga kali. Panggilan kedua untuk AddTransientHttpErrorPolicy menambahkan kebijakan pemutus sirkuit. Permintaan eksternal lebih lanjut diblokir selama 30 detik jika lima upaya yang gagal terjadi secara berurutan. Kebijakan pemutus sirkuit bersifat stateful. Semua panggilan melalui klien ini memiliki status sirkuit yang sama.
Menambahkan kebijakan dari registri Polly
Pendekatan untuk mengelola kebijakan yang digunakan secara teratur adalah menentukannya sekali dan mendaftarkannya dengan PolicyRegistry. Metode ekstensi disediakan yang memungkinkan handler ditambahkan menggunakan kebijakan dari registri:
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regulartimeouthandler")
.AddPolicyHandlerFromRegistry("regular");
Dalam kode sebelumnya, dua kebijakan didaftarkan ketika PolicyRegistry ditambahkan ke ServiceCollection. Untuk menggunakan kebijakan dari registri, metode AddPolicyHandlerFromRegistry digunakan dengan meneruskan nama kebijakan yang akan diterapkan.
Informasi lebih lanjut tentang IHttpClientFactory dan integrasi Polly dapat ditemukan di wiki Polly.
HttpClient dan manajemen siklus hidup
Instans HttpClient baru dikembalikan setiap kali CreateClient dipanggil pada IHttpClientFactory. Ada satu HttpMessageHandler untuk setiap klien bernama. Pabrik mengelola masa pakai instans HttpMessageHandler.
IHttpClientFactory mengumpulkan instans HttpMessageHandler yang dibuat oleh pabrik untuk mengurangi konsumsi sumber daya. Instans HttpMessageHandler dapat digunakan kembali dari pool saat membuat instans HttpClient baru jika masa pakainya belum kedaluwarsa.
Pengelompokan handler diinginkan karena setiap handler biasanya mengelola koneksi HTTP yang mendasari secara mandiri. Membuat lebih banyak handler daripada yang dibutuhkan dapat mengakibatkan penundaan koneksi. Beberapa pengelola juga menjaga koneksi tetap terbuka untuk waktu yang tidak terbatas, yang dapat mencegah pengelola bereaksi terhadap perubahan DNS.
Masa pakai handler default adalah dua menit. Nilai default dapat diubah untuk setiap klien yang diberi nama. Untuk mengesampingkannya, panggil SetHandlerLifetime pada IHttpClientBuilder yang dikembalikan saat membuat klien:
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Pembuangan klien tidak diperlukan. Proses pembuangan membatalkan permintaan keluar dan menjamin instans HttpClient yang diberikan tidak dapat digunakan setelah memanggil Dispose.
IHttpClientFactory melacak dan membuang sumber daya yang digunakan oleh instans HttpClient. Instans HttpClient umumnya dapat diperlakukan sebagai objek .NET yang tidak memerlukan pembuangan.
Menjaga satu instans HttpClient tetap hidup untuk durasi yang lama adalah pola umum yang digunakan sebelum adanya IHttpClientFactory. Pola ini menjadi tidak perlu setelah bermigrasi ke IHttpClientFactory.
Alternatif untuk IHttpClientFactory
Menggunakan IHttpClientFactory dalam aplikasi yang diaktifkan DI menghindari:
- Masalah kelelahan sumber daya karena pengumpulan instans
HttpMessageHandler. - Mengatasi masalah DNS yang kedaluwarsa dengan mereset
HttpMessageHandlerinstans secara berkala.
Ada cara alternatif untuk menyelesaikan masalah sebelumnya menggunakan instans SocketsHttpHandler yang berusia panjang.
- Buat instans
SocketsHttpHandlersaat aplikasi dimulai dan gunakan untuk masa pakai aplikasi. - Konfigurasikan PooledConnectionLifetime ke nilai yang sesuai berdasarkan waktu refresh DNS.
- Buat
HttpClientinstance menggunakannew HttpClient(handler, disposeHandler: false)sesuai kebutuhan.
Pendekatan sebelumnya menyelesaikan masalah manajemen sumber daya yang IHttpClientFactory diselesaikan dengan cara yang sama.
- Koneksi
SocketsHttpHandlerberbagi antar instansHttpClient. Berbagi ini mencegah kelelahan soket. - Siklus
SocketsHttpHandlerkoneksi sesuaiPooledConnectionLifetimeuntuk menghindari masalah DNS usang.
Cookies
Instans yang dikumpulkan HttpMessageHandler menghasilkan CookieContainer objek yang dibagikan. Berbagi objek yang tidak diantisipasi CookieContainer sering kali menghasilkan kode yang tidak benar. Untuk aplikasi yang memerlukan cookie, pertimbangkan:
- Menonaktifkan penanganan otomatis cookie
- Menghindari
IHttpClientFactory
Panggilan ConfigurePrimaryHttpMessageHandler untuk menonaktifkan penanganan otomatis cookie :
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Logging
Klien yang dibuat melalui IHttpClientFactory mencatat pesan log untuk semua permintaan. Aktifkan tingkat informasi yang sesuai dalam konfigurasi pengelogan Anda untuk melihat pesan log default. Pengelogan tambahan, seperti pengelogan header permintaan, hanya disertakan pada tingkat pelacakan.
Kategori log yang digunakan untuk setiap klien menyertakan nama klien. Klien bernama MyNamedClient, misalnya, mencatat pesan dengan kategori System.Net.Http.HttpClient.MyNamedClient.LogicalHandler. Pesan yang dibubuhi dengan LogicalHandler terjadi di luar pipeline penanganan permintaan. Pada saat permintaan, pesan dicatat sebelum penangan lain dalam jalur telah memprosesnya. Pada respons, pesan dicatat setelah handler alur lain menerima respons.
Pengelogan juga terjadi di dalam alur handler permintaan.
Dalam contoh MyNamedClient, pesan tersebut dicatat terhadap kategori System.Net.Http.HttpClient.MyNamedClient.ClientHandlerlog . Untuk permintaan, ini terjadi setelah semua handler lain berjalan dan segera sebelum permintaan dikirimkan di jaringan. Pada respons, pengelogan ini mencakup status respons sebelum melewati kembali melalui alur handler.
Mengaktifkan pengelogan di luar dan di dalam alur memungkinkan inspeksi perubahan yang dilakukan oleh penangan alur lainnya. Ini mungkin termasuk perubahan pada header permintaan, misalnya, atau ke kode status respons.
Menyertakan nama klien dalam kategori log memungkinkan pemfilteran log untuk klien bernama tertentu jika perlu.
Mengonfigurasi HttpMessageHandler
Anda mungkin perlu mengontrol konfigurasi HttpMessageHandler bagian dalam yang digunakan oleh klien.
IHttpClientBuilder dikembalikan saat menambahkan klien bernama atau berjenis. Metode ekstensi ConfigurePrimaryHttpMessageHandler dapat digunakan untuk menentukan delegat. Delegasi dipakai untuk membuat dan mengonfigurasi primer HttpMessageHandler yang digunakan oleh klien tersebut:
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
Menggunakan IHttpClientFactory di aplikasi konsol
Di aplikasi konsol, tambahkan referensi paket berikut ke proyek:
Dalam contoh berikut:
- IHttpClientFactory terdaftar dalam wadah layanan Generic Host.
-
MyServicemembuat instans pabrik klien dari layanan, yang digunakan untuk membuatHttpClient.HttpClientdigunakan untuk mengambil halaman web. - Metode layanan
GetPagedijalankan untuk menulis 500 karakter pertama konten halaman web ke konsol. Untuk informasi selengkapnya tentang pemanggilan layanan dariProgram.Main, lihat Dependency Injection di ASP.NET Core.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<IMyService, MyService>();
}).UseConsoleLifetime();
var host = builder.Build();
try
{
var myService = host.Services.GetRequiredService<IMyService>();
var pageContent = await myService.GetPage();
Console.WriteLine(pageContent.Substring(0, 500));
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
return 0;
}
public interface IMyService
{
Task<string> GetPage();
}
public class MyService : IMyService
{
private readonly IHttpClientFactory _clientFactory;
public MyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetPage()
{
// Content from BBC One: Dr. Who website (©BBC)
var request = new HttpRequestMessage(HttpMethod.Get,
"https://www.bbc.co.uk/programmes/b006q2x0");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return $"StatusCode: {response.StatusCode}";
}
}
}
}
Middleware penyebaran header
Penyebaran header adalah middleware yang didukung komunitas untuk menyebarluaskan header HTTP dari permintaan masuk ke permintaan Klien HTTP keluar. Untuk menggunakan penyebaran header:
Referensikan port yang didukung komunitas dari paket HeaderPropagation. ASP.NET Core 3.1 atau yang lebih baru mendukung Microsoft. AspNetCore.HeaderPropagation.
Konfigurasikan middleware dan
HttpClientdiStartup:public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddHttpClient("MyForwardingClient").AddHeaderPropagation(); services.AddHeaderPropagation(options => { options.Headers.Add("X-TraceId"); }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseHttpsRedirection(); app.UseHeaderPropagation(); app.UseMvc(); }Klien menyertakan header yang dikonfigurasi pada permintaan keluar:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);
Sumber daya tambahan
ASP.NET Core