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
Typed request context middleware for ASP.NET Core.
4
4
5
-
Define *per-endpoint* (or per-route-group) strongly-typed “request context” objects, extract them from claims/headers, and access them anywhere in the request pipeline via an `AsyncLocal` accessor.
5
+
Define *per-endpoint* (or per-route-group) strongly-typed "request context" objects, extract them from claims/headers, validate them, and access them anywhere in the request pipeline via an `AsyncLocal` accessor — including non-HTTP flows like queues and background jobs.
6
6
7
7
This repo produces two NuGet packages:
8
8
9
-
-**`TypedRequestContext`** — core middleware, attributes, accessor, correlation ID
In many services you end up needing the same business identifiers everywhere (tenant, user, role, operation id, etc.). Passing them manually through every method is noisy, and extracting them ad-hoc in each endpoint is easy to get wrong.
17
17
18
-
`TypedRequestContext`gives you:
18
+
`TypedRequestContext`solves this by letting you:
19
19
20
-
- A **typed model** for the “ambient” request context (per endpoint/group)
21
-
-**One extraction pipeline** (claims/headers) with clear “required vs optional” semantics
22
-
-**Multiple context shapes in the same service** — register many context types and choose per endpoint / route group
23
-
-**Ambient access** for deep services (inject the context type directly *or* use `IRequestContextAccessor`)
24
-
- Optional **propagation across services and async boundaries** (HTTP headers, queue/event metadata)
25
-
-**Correlation ID propagation** via `x-correlation-id` when the propagation package is enabled
20
+
-**Define once, use everywhere** — declare a typed model for your request context and access it from any layer without manual plumbing
21
+
-**Fail fast on bad input** — required values are enforced at the edge; validation errors return structured responses before your handler runs
22
+
-**Support multiple context shapes** — different endpoints can require different context types in the same service
23
+
-**Propagate context across boundaries** — carry tenant, user, and correlation data to downstream HTTP calls, queues, and background jobs without ad-hoc serialization
@@ -156,7 +154,7 @@ public sealed class BillingService(CustomerRequestContext ctx)
156
154
}
157
155
```
158
156
159
-
2) Use `IRequestContextAccessor` when you need a looser coupling (e.g., interface-based access, optional access, libraries that shouldn’t depend on a specific context type):
157
+
2) Use `IRequestContextAccessor` when you need a looser coupling (e.g., interface-based access, optional access, libraries that shouldn't depend on a specific context type):
160
158
161
159
```csharp
162
160
usingTypedRequestContext;
@@ -179,6 +177,7 @@ public sealed class AuditService(IRequestContextAccessor accessor)
179
177
- You attach the desired context type to endpoints/groups via `WithRequestContext<TContext>()`.
180
178
- For requests hitting such endpoints, the middleware:
181
179
- Creates the context via an extractor (`IRequestContextExtractor<TContext>`)
180
+
- Validates it (if validation is enabled)
182
181
- Stores it into `IRequestContextAccessor.Current` for the request lifetime
183
182
184
183
The default extractor uses cached reflection and the attributes on your context properties.
@@ -188,7 +187,7 @@ The default extractor uses cached reflection and the attributes on your context
188
187
If a `[RequiredContextValue]` property is missing:
189
188
190
189
- For **claims**: the middleware returns **401**
191
-
- For **headers**: the middleware returns **403**
190
+
- For **headers**: the middleware returns **400**
192
191
193
192
The response body is JSON:
194
193
@@ -198,6 +197,98 @@ The response body is JSON:
198
197
199
198
---
200
199
200
+
## Validation
201
+
202
+
Enable validation per context type during registration:
@@ -296,52 +387,63 @@ public sealed class DownstreamClient(
296
387
297
388
Because `GetCurrentHeaders()` includes `x-correlation-id` (when correlation is enabled), a downstream ASP.NET Core service using `AddCorrelationId()` will automatically pick it up on inbound requests.
298
389
299
-
### Non-HTTP: deserialize from metadata
390
+
### Non-HTTP flows: queues, events, background jobs
300
391
301
-
For queues/events/background jobs, you can serialize and carry the same headers dictionary as message metadata.
392
+
For non-HTTP consumers, `IRequestContextPropagator<T>` handles deserialization, validation, and context lifecycle in a single call.
302
393
303
-
Producer side (create metadata):
394
+
**Producer side** — serialize current context into message metadata:
// Context is automatically cleared when the scope is disposed
340
431
}
341
432
}
342
433
```
343
434
344
-
The same metadata can include `x-correlation-id` (from `GetCurrentHeaders()`). How you apply correlation in a non-HTTP consumer depends on your hosting model and logging setup.
435
+
`Propagate()` will:
436
+
1. Deserialize the metadata into the typed context using `IRequestContextDeserializer<T>`
437
+
2. Validate the context (if validation is enabled for this type)
438
+
3. Set it on `IRequestContextAccessor`
439
+
4. Return a disposable scope that clears the context on disposal
440
+
441
+
If validation is enabled and the validator has scoped dependencies, pass a scoped `IServiceProvider`:
0 commit comments