Ledbim.Http
1.2.0
dotnet add package Ledbim.Http --version 1.2.0
NuGet\Install-Package Ledbim.Http -Version 1.2.0
<PackageReference Include="Ledbim.Http" Version="1.2.0" />
<PackageVersion Include="Ledbim.Http" Version="1.2.0" />
<PackageReference Include="Ledbim.Http" />
paket add Ledbim.Http --version 1.2.0
#r "nuget: Ledbim.Http, 1.2.0"
#:package Ledbim.Http@1.2.0
#addin nuget:?package=Ledbim.Http&version=1.2.0
#tool nuget:?package=Ledbim.Http&version=1.2.0
Ledbim.Http
<p align="center"> <img src="ledbim_http_logo.png" alt="Ledbim.Http" width="120" /> </p>
<p align="center"> <strong>Typed HTTP Client · Result Pattern · Multipart Form Data · Named Clients</strong><br/> Ledbim ekosistemi için dış servis iletişimi altyapısı. </p>
Kapsam
Ledbim.Http paketi şu bileşenleri sağlar:
| Bileşen | Açıklama |
|---|---|
| IHttpResultClient | HTTP isteklerini Result<T> döndüren typed client arayüzü |
| HttpResultClient | GET, POST JSON, POST form data ve generic send implementasyonu |
| FormFilePart | Multipart form data için dosya tanımlama kaydı |
| AddHttpResultClient() | Tek satır DI kaydı |
Kurulum
dotnet add package Ledbim.Http
Bağımlılıklar
Microsoft.Extensions.Http v10.0.0
Ledbim.Core (project reference)
Hızlı Başlangıç
1. Program.cs
// IHttpResultClient ve IHttpClientFactory'yi kaydeder
builder.Services.AddHttpResultClient();
// Named client'ları tanımla
builder.Services.AddHttpClient("PaymentApi", client =>
{
client.BaseAddress = new Uri("https://payment.example.com");
client.DefaultRequestHeaders.Add("X-Api-Key", config["PaymentApi:ApiKey"]);
});
builder.Services.AddHttpClient("NotificationApi", client =>
{
client.BaseAddress = new Uri("https://notifications.example.com");
});
2. Handler'da Kullanım
public class ProcessPaymentCommandHandler(IHttpResultClient httpClient)
: IRequestHandler<ProcessPaymentCommand, Result<PaymentResponse>>
{
public async Task<Result<PaymentResponse>> Handle(
ProcessPaymentCommand request, CancellationToken ct)
{
var result = await httpClient.PostJsonAsync<PaymentResponse>(
clientName: "PaymentApi",
url: "/api/payments",
body: new { Amount = request.Amount, CardToken = request.CardToken },
ct);
if (!result.IsSuccess)
return Result<PaymentResponse>.Fail(result.ResponseType, result.Message, result.Errors);
return Result<PaymentResponse>.Success(ResultType.Success, result.Data!);
}
}
IHttpResultClient
public interface IHttpResultClient
{
// GET isteği
Task<Result<TResponse>> GetAsync<TResponse>(
string clientName,
string url,
IDictionary<string, string?>? headers = null,
CancellationToken cancellationToken = default);
// POST — JSON body
Task<Result<TResponse>> PostJsonAsync<TResponse>(
string clientName,
string url,
object? body,
IDictionary<string, string?>? headers = null,
CancellationToken cancellationToken = default);
// POST — Multipart form data
Task<Result<TResponse>> PostFormDataAsync<TResponse>(
string clientName,
string url,
IDictionary<string, string?>? fields = null,
IEnumerable<FormFilePart>? files = null,
IDictionary<string, string?>? headers = null,
CancellationToken cancellationToken = default);
// Generic — tüm HTTP metodları
Task<Result<TResponse>> SendAsync<TResponse>(
string clientName,
HttpMethod method,
string url,
object? body = null,
IDictionary<string, string?>? headers = null,
CancellationToken cancellationToken = default);
}
Named Client Zorunluluğu
Her metod clientName parametresini zorunlu olarak alır. İstek, bu ada karşılık gelen HttpClient üzerinden gönderilir.
// Kayıt
builder.Services.AddHttpClient("InventoryApi", client =>
{
client.BaseAddress = new Uri("https://inventory.example.com/api/");
client.Timeout = TimeSpan.FromSeconds(30);
});
// Kullanım
var result = await httpClient.GetAsync<List<ProductDto>>(
clientName: "InventoryApi",
url: "products");
Named client'ı kaydetmeden kullanmaya çalışmak InvalidOperationException fırlatır.
Result Mapping
HTTP response otomatik olarak Result<T>'ye dönüştürülür.
| HTTP Kodu | Result Karşılığı |
|---|---|
| 200 OK | Result<T>.Success(ResultType.Success, data) |
| 201 Created | Result<T>.Success(ResultType.Created, data) |
| 204 No Content | Result<T>.Success(ResultType.NoContent, default) |
| 400 Bad Request | Result<T>.Fail(ResultType.BadRequest, message, errors?) |
| 401 Unauthorized | Result<T>.Fail(ResultType.Unauthorized, message) |
| 403 Forbidden | Result<T>.Fail(ResultType.Forbidden, message) |
| 404 Not Found | Result<T>.Fail(ResultType.NotFound, message) |
| 409 Conflict | Result<T>.Fail(ResultType.Conflict, message) |
| 422 Unprocessable Entity | Result<T>.Fail(ResultType.UnprocessableEntity, message, errors?) |
| 429 Too Many Requests | Result<T>.Fail(ResultType.TooManyRequests, message) |
| 5xx / Bilinmeyen | Result<T>.Fail(ResultType.InternalServerError, message) |
Hata Response Yapısı
Uzak API'nin döndürdüğü hata body'si otomatik parse edilir:
{
"message": "Validation failed",
"errors": [
{
"propertyName": "amount",
"errorMessage": "Amount must be greater than 0",
"errorCode": "GreaterThan"
}
]
}
message yoksa "{statusCode} {reasonPhrase}" varsayılan mesaj kullanılır.
TResponse == string
TResponse olarak string verilirse response body doğrudan string olarak döndürülür — JSON deserialize yapılmaz.
var result = await httpClient.GetAsync<string>("MyApi", "/api/health");
// result.Data = "{ \"status\": \"healthy\" }"
Exception Handling
Ağ hataları exception fırlatmak yerine Result.Fail olarak döner:
| Exception | ErrorCode |
|---|---|
TaskCanceledException (timeout) |
"RequestTimeout" |
HttpRequestException |
"HttpRequestFailed" |
| JSON deserialize hatası | "ResponseDeserializationFailed" |
| Diğer | "UnexpectedHttpClientError" |
var result = await httpClient.GetAsync<OrderDto>("OrderApi", "/api/orders/123");
if (!result.IsSuccess)
{
// result.ResponseType, result.Message, result.Errors
logger.LogWarning("Sipariş alınamadı: {Message}", result.Message);
return Result.Fail(result.ResponseType, result.Message);
}
FormFilePart — Multipart Form Data
Dosya yükleme işlemleri FormFilePart kaydı ile tanımlanır:
public sealed record FormFilePart(
string Name, // Form field adı
Stream Content, // Dosya stream'i
string FileName, // Content-Disposition'daki dosya adı
string? ContentType, // MIME type (opsiyonel)
bool LeaveOpen = false // Stream'i dispose etme — default: false
);
LeaveOpen Nüansı
MultipartFormDataContent dispose edildiğinde içindeki stream'leri de kapatır. LeaveOpen = true verilirse stream bir wrapper ile korunur ve dispose çağrısına kapatılmaz.
// LeaveOpen = false (varsayılan) — stream işlem sonrası kapatılır
var filePart = new FormFilePart(
Name: "document",
Content: File.OpenRead("report.pdf"),
FileName: "monthly-report.pdf",
ContentType: "application/pdf");
// LeaveOpen = true — stream açık kalır, sonra tekrar kullanılabilir
var memStream = new MemoryStream(fileBytes);
var filePart = new FormFilePart(
Name: "avatar",
Content: memStream,
FileName: "profile.jpg",
ContentType: "image/jpeg",
LeaveOpen: true);
// İstek sonrası memStream hâlâ kullanılabilir
memStream.Position = 0;
Kullanım Örneği
// Text field'lar + dosya birlikte
var fields = new Dictionary<string, string?>
{
{ "userId", userId.ToString() },
{ "description", "Aylık rapor" }
};
var files = new[]
{
new FormFilePart(
Name: "report",
Content: reportStream,
FileName: "report-march.pdf",
ContentType: "application/pdf")
};
var result = await httpClient.PostFormDataAsync<UploadResponse>(
clientName: "StorageApi",
url: "/api/documents",
fields: fields,
files: files,
ct: ct);
JSON Serializasyon Ayarları
Tüm istek ve yanıtlarda sabit seçenekler kullanılır:
JsonNamingPolicy = JsonNamingPolicy.CamelCase // İstek body'si camelCase serialize edilir
PropertyNameCaseInsensitive = true // Yanıt deserialize'ında büyük/küçük harf duyarsız
Özel Header Gönderme
Tüm metodlar headers parametresini destekler:
var headers = new Dictionary<string, string?>
{
{ "X-Tenant-Id", tenantId },
{ "X-Correlation-Id", correlationId },
{ "Accept-Language", "tr-TR" }
};
var result = await httpClient.GetAsync<UserDto>(
clientName: "UserApi",
url: $"/api/users/{userId}",
headers: headers,
cancellationToken: ct);
SendAsync — Generic Metod
GET ve POST dışındaki HTTP metodları için:
// PUT
var result = await httpClient.SendAsync<UpdateResponse>(
clientName: "ProductApi",
method: HttpMethod.Put,
url: $"/api/products/{id}",
body: new { Name = "Yeni Ad", Price = 99.90m },
ct);
// DELETE
var result = await httpClient.SendAsync<object>(
clientName: "ProductApi",
method: HttpMethod.Delete,
url: $"/api/products/{id}",
ct: ct);
// PATCH
var result = await httpClient.SendAsync<PatchResponse>(
clientName: "OrderApi",
method: HttpMethod.Patch,
url: $"/api/orders/{id}/status",
body: new { Status = "Shipped" },
ct: ct);
Mimari Notlar
- Transport-agnostic sonuç: Tüm metodlar
Result<T>döner — çağıran kod HTTP ayrıntısını bilmez, yalnızcaIsSuccessveDataile çalışır. IHttpClientFactoryüzerinden çalışır:HttpClientinstance'ları doğrudan new'lenmez; socket exhaustion ve DNS yenileme sorunları otomatik bertaraf edilir.- Named client zorunlu: Her çağrıda
clientNameparametresi girilmelidir. Bu, aynıIHttpResultClientinstance'ından birden fazla servis için istek yapılabilmesini sağlar. - Partial class yapısı:
HttpResultClientdört parçalıdır (HttpResultClient.cs,Send.cs,FormData.cs,Helpers.cs) — her concern ayrı dosyada tutulmuştur.
Lisans
Bu paket Ledbim Bilişim tarafından geliştirilmektedir. Ticari kullanım için lisans bilgisi için iletişime geçiniz.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Ledbim.Core (>= 1.2.0)
- Microsoft.Extensions.Http (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.0 | 152 | 3/28/2026 |