{"id":44093148,"url":"https://github.com/fuznio/fluenthttp","last_synced_at":"2026-04-06T20:01:20.880Z","repository":{"id":336518100,"uuid":"1141394831","full_name":"FuznIO/FluentHttp","owner":"FuznIO","description":"Lightweight fluent API on top of HttpClient for building and sending HTTP requests with a clean, chainable syntax.","archived":false,"fork":false,"pushed_at":"2026-04-05T21:03:04.000Z","size":175,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T23:10:56.952Z","etag":null,"topics":["api-client","csharp","dotnet","fluent-api","http","httpclient","networking","rest","serialization","testing"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FuznIO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-24T19:23:41.000Z","updated_at":"2026-04-05T21:01:51.000Z","dependencies_parsed_at":"2026-04-05T23:02:28.447Z","dependency_job_id":null,"html_url":"https://github.com/FuznIO/FluentHttp","commit_stats":null,"previous_names":["fuznio/fluenthttp"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/FuznIO/FluentHttp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuznIO%2FFluentHttp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuznIO%2FFluentHttp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuznIO%2FFluentHttp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuznIO%2FFluentHttp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FuznIO","download_url":"https://codeload.github.com/FuznIO/FluentHttp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuznIO%2FFluentHttp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31487542,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-client","csharp","dotnet","fluent-api","http","httpclient","networking","rest","serialization","testing"],"created_at":"2026-02-08T11:23:16.764Z","updated_at":"2026-04-06T20:01:20.875Z","avatar_url":"https://github.com/FuznIO.png","language":"C#","readme":"# Fuzn.FluentHttp\n\nA lightweight fluent API for building and sending HTTP requests with `HttpClient`. Provides a clean, chainable interface for configuring URLs, headers, content types, authentication, and serialization.\n\n## Installation\n\nTo get started, add the Fuzn.FluentHttp package to your project using the following command:\n\n```bash\ndotnet add package Fuzn.FluentHttp\n```\n\n## Quick Start\n\nThe following example demonstrates how to register an `HttpClient` with dependency injection and use it to make a simple GET request:\n\n```csharp\nusing Fuzn.FluentHttp;\n\n// Register HttpClient with DI\nservices.AddHttpClient\u003cUserHttpClient\u003e(client =\u003e\n{\n    client.BaseAddress = new Uri(\"https://api.example.com\");\n});\n\n// Use in your service\npublic class UserHttpClient(HttpClient httpClient)\n{\n    public async Task\u003cUser?\u003e GetUserAsync(int id)\n    {\n        var response = await httpClient\n            .Url($\"/users/{id}\")\n            .Get\u003cUser\u003e();\n\n        return response.IsSuccessful ? response.Data : null;\n    }\n}\n```\n\n**Alternative syntax** using `Request().WithUrl()`:\n```csharp\nvar response = await httpClient.Request().WithUrl(\"/users/1\").Get\u003cUser\u003e();\n```\n\n\u003e **Note:** When `HttpClient` has no `BaseAddress`, you must use absolute URLs.\n\n## HTTP Methods\n\nAll standard HTTP methods are supported with both generic and non-generic versions. Generic methods automatically deserialize the response body:\n\n```csharp\n// Non-generic returns FluentHttpResponse\nawait httpClient.Url(\"/resource\").Get();\nawait httpClient.Url(\"/resource\").Post();\nawait httpClient.Url(\"/resource\").Put();\nawait httpClient.Url(\"/resource\").Patch();\nawait httpClient.Url(\"/resource\").Delete();\nawait httpClient.Url(\"/resource\").Head();\nawait httpClient.Url(\"/resource\").Options();\n\n// Generic returns FluentHttpResponse\u003cT\u003e with deserialized Data property\nawait httpClient.Url(\"/resource\").Get\u003cT\u003e();\nawait httpClient.Url(\"/resource\").Post\u003cT\u003e();\n\n// Custom HTTP methods (e.g., WebDAV)\nawait httpClient.Url(\"/resource\").Send(new HttpMethod(\"PROPFIND\"));\nawait httpClient.Url(\"/resource\").Send\u003cT\u003e(new HttpMethod(\"MKCOL\"));\n```\n\n## Request Configuration\n\n### Content\n\nObjects are automatically serialized to JSON:\n\n```csharp\nawait httpClient\n    .Url(\"/users\")\n    .WithContent(new { Name = \"John\", Email = \"john@example.com\" })\n    .Post\u003cUser\u003e();\n```\n\n### Query Parameters\n\nAdd query parameters to the request URL:\n\n```csharp\n// Individual parameters (values must be strings)\n.WithQueryParam(\"q\", \"dotnet\")\n.WithQueryParam(\"page\", \"1\")\n\n// Multiple values for same key (e.g., ?tags=c%23\u0026tags=dotnet)\n.WithQueryParam(\"tags\", \"c#\")\n.WithQueryParam(\"tags\", \"dotnet\")\n\n// For non-string values, convert to string yourself\n.WithQueryParam(\"date\", DateTime.UtcNow.ToString(\"O\"))\n.WithQueryParam(\"active\", true.ToString().ToLower())\n```\n\n### Headers\n\n```csharp\n.WithHeader(\"X-Custom\", \"value\")\n.WithHeaders(new Dictionary\u003cstring, string\u003e { [\"X-Another\"] = \"value\" })\n```\n\n### Authentication\n\nBuilt-in support for common authentication schemes:\n\n```csharp\n.WithAuthBearer(\"jwt-token\")\n.WithAuthBasic(\"username\", \"password\")\n.WithAuthApiKey(\"api-key\")                    // Uses X-API-Key header\n.WithAuthApiKey(\"api-key\", \"Authorization\")   // Custom header name\n```\n\n### Content \u0026 Accept Types\n\nControl request and response content types:\n\n```csharp\n.WithContentType(ContentTypes.Json)\n.WithContentType(\"application/graphql\")\n.WithAccept(AcceptTypes.Json)\n.WithAccept(\"application/pdf\")\n```\n\n### File Uploads\n\nUpload files with automatic multipart/form-data handling:\n\n```csharp\nawait httpClient\n    .Url(\"/upload\")\n    .WithFile(\"file\", \"doc.pdf\", fileStream, \"application/pdf\")\n    .WithFormField(\"description\", \"My document\")\n    .Post\u003cUploadResult\u003e();\n```\n\n### Other Options\n\n```csharp\n.WithTimeout(TimeSpan.FromSeconds(30))\n.WithUserAgent(\"MyApp/1.0\")\n.WithCookie(\"session\", \"abc123\")\n.WithVersion(HttpVersion.Version20)\n.WithVersionPolicy(HttpVersionPolicy.RequestVersionExact)\n.WithCancellationToken(cancellationToken)\n```\n\n## Working with Responses\n\n### FluentHttpResponse / FluentHttpResponse\u0026lt;T\u0026gt;\n\nResponses provide easy access to status, content, headers, and cookies. The response body is only deserialized when you access the `Data` property, not automatically upon receiving the response:\n\n```csharp\nvar response = await httpClient.Url(\"/users/1\").Get\u003cUser\u003e();\n\n// Check status\nif (response.IsSuccessful)\n{\n    User user = response.Data!;  // Deserialization happens here\n}\n\n// Or throw HttpRequestException on failure\nresponse.EnsureSuccessful();\n\n// Access response properties\nHttpStatusCode status = response.StatusCode;\nstring? reason = response.ReasonPhrase;\nstring content = response.Content;\nstring? contentType = response.ContentType;\nlong? contentLength = response.ContentLength;\nVersion version = response.Version;\n\n// Access headers and cookies\nvar headers = response.Headers;\nvar contentHeaders = response.ContentHeaders;\nvar cookies = response.Cookies;\n\n// Access underlying messages\nHttpResponseMessage inner = response.InnerResponse;\nHttpRequestMessage request = response.RequestMessage;\n\n// Deserialize to different type (useful for error responses)\nvar error = response.ContentAs\u003cProblemDetails\u003e();\n\n// Try deserialize without throwing\nif (response.TryContentAs\u003cUser\u003e(out var user))\n{\n    // Use user\n}\n```\n\n### Streaming Responses\n\nFor large files, use streaming to avoid loading the entire response into memory. The `FluentHttpStreamResponse` must be disposed after use:\n\n```csharp\nawait using var response = await httpClient.Url(\"/files/large.zip\").GetStream();\n\nif (response.IsSuccessful)\n{\n    // Access metadata\n    long? size = response.ContentLength;\n    string? type = response.ContentType;\n    string? fileName = response.FileName;  // From Content-Disposition header\n\n    // Read as stream or bytes\n    var stream = await response.GetStream();\n    // Or: var bytes = await response.GetBytes();\n}\n```\n\n## Serialization\n\nBy default, FluentHttp uses `System.Text.Json` with `JsonSerializerDefaults.Web` (camelCase, case-insensitive). No configuration is needed for standard JSON APIs.\n\n### Changing the Default Serializer\n\nThe default serializer is used when no content-type-specific serializer matches. To customize JSON serialization options, replace the default with a new `SystemTextJsonSerializerProvider` configured with your options:\n\n```csharp\n// Change JSON options (e.g., use PascalCase instead of camelCase)\nFluentHttpDefaults.Serializers.Default = new SystemTextJsonSerializerProvider(\n    new JsonSerializerOptions { PropertyNamingPolicy = null });\n\n// Or swap to a completely different serializer\nFluentHttpDefaults.Serializers.Default = new NewtonsoftSerializerProvider();\n```\n\n### Custom Serializer\n\nImplement `ISerializerProvider` to use a different serialization library:\n\n```csharp\npublic class NewtonsoftSerializerProvider : ISerializerProvider\n{\n    public string Serialize\u003cT\u003e(T obj) =\u003e JsonConvert.SerializeObject(obj);\n    public T? Deserialize\u003cT\u003e(string json) =\u003e JsonConvert.DeserializeObject\u003cT\u003e(json);\n}\n\nFluentHttpDefaults.Serializers.Default = new NewtonsoftSerializerProvider();\n```\n\n### Serializers by Content Type\n\nRegister serializers for specific content types. The correct serializer is automatically selected based on the request's content type for serialization and the response's `Content-Type` header for deserialization:\n\n```csharp\nFluentHttpDefaults.Serializers\n    .Register(\"application/json\", new SystemTextJsonSerializerProvider())\n    .Register(\"application/xml\", new XmlSerializerProvider());\n```\n\n### Per-Request Overrides\n\nOverride serializer resolution for a single request. You can override by content type or override all resolution entirely:\n\n```csharp\n// Override by content type\nclient.Url(\"/api/data\")\n    .WithSerializer(\"application/json\", new SystemTextJsonSerializerProvider(myJsonOptions))\n    .WithSerializer(\"application/xml\", new XmlSerializerProvider())\n    .WithContentType(\"application/xml\")\n    .WithContent(payload)\n    .Post\u003cMyResponse\u003e();\n\n// Override all serializer resolution for both request and response\nclient.Url(\"/api/data\")\n    .WithSerializer(new MyCustomSerializer())\n    .Get();\n```\n\n### Serializer Resolution Order\n\n1. Per-request `WithSerializer()` (overrides everything)\n2. Per-request registry (via `WithSerializer(contentType, serializer)`)\n3. Global registry (`FluentHttpDefaults.Serializers`)\n4. Default (`FluentHttpDefaults.Serializers.Default`)\n\n## Resilience \u0026 Retry\n\nFluentHttp works seamlessly with `HttpClient`'s `DelegatingHandler` pipeline. Use libraries like Polly for retry policies, circuit breakers, and other resilience patterns:\n\n```csharp\nservices.AddHttpClient(\"MyApi\")\n    .AddStandardResilienceHandler();  // Microsoft.Extensions.Http.Resilience\n    // Or: .AddTransientHttpErrorPolicy(...) // Microsoft.Extensions.Http.Polly\n```\n\n## Debugging\n\nBoth requests and responses override `ToString()` for easy inspection:\n\n```csharp\n// Inspect request configuration\nvar builder = httpClient.Url(\"/users\").WithAuthBearer(\"token\");\nConsole.WriteLine(builder);  // Prints formatted request details (auth is redacted)\n\n// Inspect response\nConsole.WriteLine(response);  // Prints status, headers, and content\n\n// Get HttpRequestMessage without sending\nvar request = builder.BuildRequest(HttpMethod.Post);\n```\n\n## License\n\nMIT License - see [LICENSE](https://github.com/FuznIO/FluentHttp/blob/main/LICENSE) for details.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuznio%2Ffluenthttp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuznio%2Ffluenthttp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuznio%2Ffluenthttp/lists"}