Ledbim.Http 1.2.0

dotnet add package Ledbim.Http --version 1.2.0
                    
NuGet\Install-Package Ledbim.Http -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ledbim.Http" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ledbim.Http" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Ledbim.Http" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ledbim.Http --version 1.2.0
                    
#r "nuget: Ledbim.Http, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ledbim.Http@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ledbim.Http&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Ledbim.Http&version=1.2.0
                    
Install as a Cake Tool

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ızca IsSuccess ve Data ile çalışır.
  • IHttpClientFactory üzerinden çalışır: HttpClient instance'ları doğrudan new'lenmez; socket exhaustion ve DNS yenileme sorunları otomatik bertaraf edilir.
  • Named client zorunlu: Her çağrıda clientName parametresi girilmelidir. Bu, aynı IHttpResultClient instance'ından birden fazla servis için istek yapılabilmesini sağlar.
  • Partial class yapısı: HttpResultClient dö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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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