Modern Yazılım Geliştirme: HttpClient ile API Entegrasyonunu Optimize Etmek
Yazılım geliştirme dünyasında, dış API’lerle iletişim kurmak artık günlük bir alışkanlık hâline geldi. Kullanıcı verilerini almak, formları göndermek ya da kayıtları güncellemek gibi işlemler için HttpClient sınıfını sıkça kullanıyoruz. Ancak, her seferinde aynı tekrarlayan kodları yazmak zorunda kalmak hem zaman kaybına neden oluyor hem de projenin okunabilirliğini düşürüyor. Bu yazıda, tekrar eden kodları ortadan kaldırarak, yeniden kullanılabilir ve dependency injection (DI) destekli bir HttpClient yapısının nasıl oluşturulacağını adım adım ele alacağız.
Hadi başlayalım!
Sorun: Tekrarlayan Kod Yığını
Birçok geliştirici, API çağrıları için aşağıdaki gibi temel bir kod parçası ile başlar:
using (var client = new HttpClient()) { var response = await client.GetAsync("https://api.ornek.com/kullanicilar"); var data = await response.Content.ReadAsStringAsync(); }
Bu yapı ilk bakışta basit ve işlevsel görünebilir. Ancak işin içine şu ihtiyaçlar girdiğinde, kod hızla karmaşık bir hâl alır:
- Farklı HTTP metotları (POST, PUT, DELETE vb.) kullanma ihtiyacı,
- JSON verilerini serileştirme veya deserileştirme gerekliliği,
- Hata yönetiminin daha esnek yapılması,
- HttpClient‘ın yeniden kullanılabilir hâle getirilmesi (yeni bir örnek oluşturmak yerine, performans sorunlarını önlemek için aynı örneği kullanma).
Bu gereksinimler, kodunuzu tekrar eden ve karmaşık bir yapıya dönüştürebilir. Artık bunu düzeltmenin zamanı geldi!
Çözüm: Yeniden Kullanılabilir HttpClient Yardımcı Sınıfı
Bu çözümde aşağıdaki özelliklere sahip bir HttpClientHelper sınıfı oluşturacağız:
- Her türlü HTTP metodunu destekleyecek (GET, POST, PUT vb.).
- İstek gövdesini JSON formatına dönüştürecek ve yanıtları güçlü tip modellerine deserialize edecek.
- İsteğe bağlı hata yönetimi sağlayacak.
- Daha iyi performans ve sürdürülebilirlik için IHttpClientFactory kullanarak dependency injection ile uyumlu olacak.
İşte çözümün kodu:
HttpClientHelper Sınıfı
using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class HttpClientHelper { private readonly IHttpClientFactory _httpClientFactory; public HttpClientHelper(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<TResponse> SendRequestAsync<TRequest, TResponse>( string clientName, string endpoint, HttpMethod method, TRequest requestBody = default, bool handleErrors = false) { // Belirtilen isimle HttpClient örneği alınıyor var client = _httpClientFactory.CreateClient(clientName); var request = new HttpRequestMessage { Method = method, RequestUri = new Uri(endpoint) }; // İstek gövdesi varsa JSON formatına dönüştür if (requestBody != null) { var json = JsonSerializer.Serialize(requestBody); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); } // İsteği gönder var response = await client.SendAsync(request); // Hataları isteğe bağlı olarak ele al if (handleErrors && !response.IsSuccessStatusCode) { throw new HttpRequestException($"HTTP isteği başarısız oldu. Durum kodu: {response.StatusCode}"); } // Yanıtı güçlü tip modeline deserialize et var responseJson = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<TResponse>(responseJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } }
Dependency Injection ile HttpClient Kullanımı
HttpClient doğru şekilde kullanılmazsa performans sorunlarına yol açabilir. Örneğin, her istek için yeni bir HttpClient örneği oluşturmak, socket exhaustion (bağlantı tükenmesi) gibi sorunlara neden olabilir. Bunun yerine, IHttpClientFactory kullanarak HttpClient‘ı yönetmek çok daha verimli bir yöntemdir.
Program.cs veya Startup.cs İçin Kayıt
using Microsoft.Extensions.DependencyInjection; var services = new ServiceCollection(); // HttpClient örneklerini merkezi olarak yapılandır services.AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri("https://api.ornek.com/"); client.DefaultRequestHeaders.Add("Accept", "application/json"); client.Timeout = TimeSpan.FromSeconds(30); }); // HttpClientHelper sınıfını ekle services.AddTransient<HttpClientHelper>(); var serviceProvider = services.BuildServiceProvider();
Bu kayıt işlemi, HttpClientHelper ve diğer bağımlılıkların herhangi bir sınıfta kolayca kullanılmasını sağlar.
API Servisleri ile Kullanım
Şimdi, yukarıdaki yardımcı sınıfı bir UserService sınıfında kullanarak dış API’lerle nasıl iletişim kuracağımızı göstereceğiz.
UserService Sınıfı
using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; public class UserService { private readonly HttpClientHelper _httpClientHelper; public UserService(HttpClientHelper httpClientHelper) { _httpClientHelper = httpClientHelper; } public async Task<List<UserModel>> GetUsersAsync() { return await _httpClientHelper.SendRequestAsync<object, List<UserModel>>( clientName: "ApiClient", endpoint: "users", method: HttpMethod.Get ); } public async Task<UserModel> CreateUserAsync(UserModel newUser) { return await _httpClientHelper.SendRequestAsync<UserModel, UserModel>( clientName: "ApiClient", endpoint: "users", method: HttpMethod.Post, requestBody: newUser ); } public async Task<bool> DeleteUserAsync(int userId) { await _httpClientHelper.SendRequestAsync<object, object>( clientName: "ApiClient", endpoint: $"users/{userId}", method: HttpMethod.Delete ); return true; } }
Kullanım Örneği: Main Method
Son olarak, bu servisi nasıl kullanacağımıza bakalım:
using System; using System.Threading.Tasks; public class Program { public static async Task Main(string[] args) { var serviceProvider = new ServiceCollection() .AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri("https://api.ornek.com/"); client.DefaultRequestHeaders.Add("Accept", "application/json"); }) .AddTransient<HttpClientHelper>() .AddTransient<UserService>() .BuildServiceProvider(); var userService = serviceProvider.GetRequiredService<UserService>(); var newUser = new UserModel { Name = "Ali Veli", Email = "[email protected]" }; var createdUser = await userService.CreateUserAsync(newUser); Console.WriteLine($"Kullanıcı oluşturuldu: {createdUser.Name}"); } }
Neden Bu Yapı Daha İyi?
- Tekrar Eden Kodlardan Kurtulma: Yardımcı sınıf tüm API çağrılarını yönetir, böylece kodunuzu temiz ve düzenli tutabilirsiniz.
- Merkezi Yapılandırma: API ayarları tek bir noktadan yönetilir.
- Performans Optimizasyonu: IHttpClientFactory kullanımı ile bağlantı tükenmesi sorunları engellenir.
- Kolay Bakım ve Test Edilebilirlik: API işlemleri servis seviyesinde ayrıştırıldığı için değişiklikler kolayca uygulanabilir.
Bu çözümle, API çağrılarınız daha temiz, daha hızlı ve daha sürdürülebilir hâle gelecek.