You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Performance, Documentation, and Standardization (#45)
* Update content extensions to use central method
* Update class and method summary comments
* Performance updates and documentation clarifications
* Update unit tests
* Update version
* Update documentation
* Add samples
* Improve READMEs
Copy file name to clipboardExpand all lines: README.md
+93Lines changed: 93 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,6 +40,99 @@ Apps targeting modern TFMs (such as .NET 5 and later) already include `System.Te
40
40
41
41
FluentHttpClient is available on [NuGet.org](https://www.nuget.org/packages/FluentHttpClient/) and can be installed using a NuGet package manager or the .NET CLI.
42
42
43
+
## When to Use FluentHttpClient
44
+
45
+
While `HttpClient` is a powerful and flexible tool, building HTTP requests with it often involves repetitive boilerplate, manual serialization, and scattered configuration logic. FluentHttpClient addresses these pain points by providing a fluent, chainable API that reduces cognitive load and improves code readability.
46
+
47
+
### Common HttpClient Challenges
48
+
49
+
**Repetitive Configuration**
50
+
Every request requires manually setting headers, query parameters, and content, often scattered across multiple lines. This makes it easy to miss required headers or forget encoding rules.
51
+
52
+
**Manual Serialization**
53
+
Converting objects to JSON, setting the correct `Content-Type`, and deserializing responses requires multiple steps and imports. Error-prone encoding and parsing logic often needs to be duplicated across your codebase.
54
+
55
+
**Inconsistent Error Handling**
56
+
Without a unified approach to handling success and failure responses, status code checks and logging logic tend to be duplicated or omitted entirely.
57
+
58
+
**Lifetime and Reuse Concerns**
59
+
Properly managing `HttpClient` lifetime, avoiding socket exhaustion, and reusing instances while still configuring per-request state requires careful planning and often leads to awkward patterns.
60
+
61
+
### How FluentHttpClient Helps
62
+
63
+
FluentHttpClient wraps `HttpClient` (you still manage the lifetime) and provides extension methods that let you configure requests in a single, readable chain:
64
+
65
+
-**Fluent Configuration**: Add headers, query parameters, cookies, and authentication in a natural, discoverable flow
66
+
-**Automatic Serialization**: Built-in JSON and XML serialization/deserialization with support for `System.Text.Json`, Native AOT, and custom options
67
+
-**Response Handlers**: Attach success and failure callbacks directly in the request chain without breaking fluency
68
+
-**Reduced Boilerplate**: Express the entire request lifecycle—configuration, sending, and deserialization—in a single expression
69
+
70
+
### Side-by-Side Comparison
71
+
72
+
Here's the same request implemented with raw `HttpClient` and FluentHttpClient:
The FluentHttpClient version expresses the same logic in fewer lines, with better readability and no loss of functionality. All configuration, sending, error handling, and deserialization happen in a single fluent chain.
135
+
43
136
## Usage and Support
44
137
45
138
- Check out the project documentation https://scottoffen.github.io/fluenthttpclient.
Copy file name to clipboardExpand all lines: docs/docs/configure-cookies.md
+72-8Lines changed: 72 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,8 +17,42 @@ var builder = client
17
17
18
18
* Throws if `name` is `null`, empty, or whitespace.
19
19
*`null` values are converted to an empty string.
20
+
* Cookie values are **automatically URL-encoded** by default using RFC 6265 encoding.
20
21
* Existing cookies with the same name are overwritten.
21
22
23
+
### Controlling Encoding
24
+
25
+
By default, cookie values are URL-encoded to ensure special characters (such as `;`, `=`, `,`, and whitespace) do not break the Cookie header format. This is recommended for most use cases.
26
+
27
+
```csharp
28
+
// Default behavior - value is URL-encoded
29
+
varbuilder=client
30
+
.UsingBase()
31
+
.WithCookie("session", "value with spaces");
32
+
33
+
// Explicit encoding control
34
+
varbuilder=client
35
+
.UsingBase()
36
+
.WithCookie("session", "value with spaces", encode: true);
FluentHttpClient lets you configure request headers directly on `HttpRequestBuilder`. All header methods add *deferred configurators* to the builder, so headers are applied when the `HttpRequestMessage` is finally built, not when you call the fluent methods.
6
+
FluentHttpClient lets you configure request headers directly on `HttpRequestBuilder`. Header configuration uses two approaches:
7
+
8
+
***String headers** (`WithHeader`, `WithHeaders`) are validated and stored immediately when called, providing fast fail-fast validation for common header scenarios.
9
+
***Typed headers** (`WithAuthentication`, `WithBasicAuthentication`, `WithOAuthBearerToken`, `ConfigureHeaders`) use deferred configurators that are applied when the `HttpRequestMessage` is built, allowing strongly-typed header configuration with complex types like `CacheControl` and `Authorization`.
7
10
8
11
## Adding Single Headers
9
12
@@ -83,11 +86,110 @@ Use the bulk overloads when you already have headers in a collection (e.g. from
83
86
84
87
:::
85
88
89
+
## Typed Headers
90
+
91
+
For headers that require strongly-typed values (such as `Authorization`, `CacheControl`, `Accept`, `IfModifiedSince`, `IfNoneMatch`, and others), use `ConfigureHeaders` to configure them directly through the `HttpRequestHeaders` API.
If multiple configurators set the same header property, the last one wins (for single-value headers like `Authorization`). For collection-based headers (like `Accept`), all values accumulate unless a configurator explicitly clears the collection.
165
+
166
+
### When to Use ConfigureHeaders
167
+
168
+
Use `ConfigureHeaders` when you need:
169
+
170
+
***Strongly-typed headers** like `Authorization`, `CacheControl`, `Accept`, `IfModifiedSince`
171
+
***Headers with quality values** (e.g., `Accept: application/json;q=0.9`)
172
+
***Headers with multiple properties** (e.g., `CacheControl` with NoCache, NoStore, MaxAge)
173
+
***Collection-based headers** that support multiple values with typed entries
174
+
***Direct access** to the `HttpRequestHeaders` API
175
+
176
+
For simple string-based headers (like `X-Correlation-Id` or `X-Tenant`), prefer `WithHeader` instead - it validates immediately, performs better, and keeps your code simpler.
177
+
178
+
### Performance Notes
179
+
180
+
`ConfigureHeaders` uses deferred execution, which means:
181
+
182
+
* Configurators are stored in a list and executed when the request is built.
183
+
* Multiple configurators have a small overhead compared to a single call.
184
+
* For high-throughput scenarios with simple headers, prefer `WithHeader`.
185
+
186
+
However, for most applications, the performance difference is negligible, and the strongly-typed API provides better compile-time safety and IntelliSense support.
187
+
86
188
## Reserved Headers
87
189
88
190
FluentHttpClient intentionally restricts a small set of HTTP headers that are controlled by the underlying `HttpClient` and its transport layers. These headers define wire-level framing and routing behavior, and overriding them can produce ambiguous requests, protocol violations, or security issues.
89
191
90
-
Because of this, the fluent header extensions do **not** allow setting the following headers:
192
+
Because of this, the fluent string-header extensions (`WithHeader` and `WithHeaders`) do **not** allow setting the following headers:
91
193
92
194
*`Host`
93
195
*`Content-Length`
@@ -97,9 +199,9 @@ These values are determined automatically based on the request URI, the configur
97
199
98
200
### Advanced Usage
99
201
100
-
This restriction only applies to the high-level fluent extensions. If advanced scenarios require manual control of these headers, you can still modify the underlying `HttpRequestMessage`using a configuration delegate (for example, via [`When`](./conditional-configuration.md) with an always-true bool or predicate). This opt-in approach allows experienced users to take full control without exposing casual users to common footguns.
202
+
This restriction only applies to the `WithHeader` and `Withheaders`fluent extensions. If advanced scenarios require manual control of these headers, you can still accomplish this using `ConfigureHeaders`. This opt-in approach allows experienced users to take full control without exposing casual users to common footguns.
101
203
102
-
In short, the fluent API keeps the safe path safe, while still leaving the door open for expert customization or tom-foolery when needed.
204
+
In short, the fluent API keeps the simple path safe, while still leaving the door open for expert customization or tom-foolery when needed.
103
205
104
206
:::tip Indirect Control
105
207
@@ -166,15 +268,33 @@ var builder = client
166
268
167
269
## Behavior Notes
168
270
169
-
All of these methods work by adding actions to `HttpRequestBuilder.HeaderConfigurators`:
271
+
### String Header Methods
272
+
273
+
`WithHeader` and `WithHeaders` methods:
274
+
275
+
* Store headers in an internal dictionary with **immediate validation**.
276
+
* Validate header keys and values when the method is called (fail-fast).
* Headers are case-insensitive (per HTTP specification).
279
+
* Multiple values for the same header key are supported and accumulated.
280
+
* Headers are applied to the `HttpRequestMessage` when the request is built.
281
+
282
+
### Typed Header Methods
283
+
284
+
`WithAuthentication`, `WithBasicAuthentication`, `WithOAuthBearerToken`, and `ConfigureHeaders`:
285
+
286
+
* Add deferred configurators to `HttpRequestBuilder.HeaderConfigurators`.
287
+
* Validation occurs when the request is built (when you call `SendAsync`).
288
+
* Multiple configurators are cumulative and applied in order.
289
+
* The last configurator that sets a particular header wins (e.g., `Authorization`).
290
+
* Configurators have direct access to strongly-typed header properties.
291
+
292
+
### General Behavior
170
293
171
-
* Headers are applied when the request is built (for example, when you call `SendAsync`).
172
-
* Multiple calls to header methods are cumulative:
173
-
* Multiple non-auth headers are combined as expected.
174
-
* The most recent method that sets `Authorization` wins.
175
-
* Headers are added via `TryAddWithoutValidation`, which:
176
-
* Skips strict header format checks.
177
-
* Still respects HTTP semantics at send time.
294
+
* All header configuration is cumulative - multiple calls add or update headers.
295
+
* String headers and typed headers can be mixed in the same request.
296
+
* Headers set via `WithHeader` take effect before typed header configurators run.
297
+
* Reserved headers (`Host`, `Content-Length`, `Transfer-Encoding`) cannot be set via string methods but may be set via advanced techniques (see Reserved Headers section).
178
298
179
299
---
180
300
@@ -186,6 +306,7 @@ All of these methods work by adding actions to `HttpRequestBuilder.HeaderConfigu
186
306
|`WithHeader(string key, IEnumerable<string> values)`| Add a header with multiple values. |
187
307
|`WithHeaders(IEnumerable<KeyValuePair<string, string>> headers)`| Add multiple headers, one value each. |
0 commit comments