Composable request filtering, filter evaluation, enforcement, and filter-event storage for ASP.NET Core applications.
Important
This project is currently pre-1.0. Public APIs, option names, defaults, and configuration behavior may change between preview releases.
This release is a breaking change for consumers upgrading from the former
broader package. Eigenverft.Routed.RequestFilters now contains request
filtering, filter evaluation/enforcement, filter events, and filter-owned event
storage only. It is not a drop-in replacement for the previous hosting stack.
Before upgrading:
- move hosting, configuration-source composition, Kestrel/SNI, certificates, static files, warm-up, health probes, redirects, general request logging, and traffic shaping to their dedicated WebLib or NetLib packages;
- remove or relocate the old configuration sections for those capabilities; this package no longer processes them;
- review collection binding and explicitly choose the intended
UseCodeDefaultsbehavior when configuration is present; - update the application to a supported target framework:
net8.0ornet10.0.
The complete removed-API list and replacement dependency guidance are in the package release notes.
| Package | Eigenverft.Routed.RequestFilters |
| Application model | ASP.NET Core middleware and dependency-injection extensions |
| Target frameworks | .NET 8 and .NET 10 |
| Configuration | IOptionsMonitor<T>, IConfiguration, or code-based delegates |
| Event storage | Null, bounded in-memory, or SQLite |
| License | MIT |
The library is designed for applications that want to compose focused request policies instead of adopting one monolithic request-processing pipeline. Each component is registered explicitly and added to the pipeline explicitly.
- Installation
- Quick start
- How filtering works
- Common recipes
- Middleware catalog
- Evaluation and event storage
- Pipeline ordering
- Build from source
- Security notes
dotnet add package Eigenverft.Routed.RequestFiltersThe package provides assets for:
net8.0net10.0
It uses the ASP.NET Core shared framework.
Every middleware follows the same basic pattern:
- Register it with
Add.... - Configure its matching and enforcement policy.
- Add it to the request pipeline with
Use....
The following example allows only the configured host names.
using Eigenverft.Routed.RequestFilters.Middleware.HostNameFiltering;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostNameFiltering();
var app = builder.Build();
app.UseHostNameFiltering();
app.MapGet("/", () => Results.Ok(new { status = "ready" }));
app.Run();{
"HostNameFilteringOptions": {
"Whitelist": [
"api.example.com",
"*.example.com"
],
"Blacklist": [],
"AllowBlacklistedRequests": false,
"AllowUnmatchedRequests": false,
"BlockStatusCode": 403
}
}The parameterless AddHostNameFiltering() registration binds the section named HostNameFilteringOptions. The example allows requests matching the whitelist and rejects unmatched or blacklisted hosts.
Caution
Registration alone is not an implicit deny policy. Many filters are intentionally observable by default. Set the relevant Allow... flags explicitly before relying on a filter for enforcement.
Most filtering middleware classifies an observed request value as one of three results:
| Result | Meaning |
|---|---|
| Whitelist | The value matched an allowed pattern. |
| Blacklist | The value matched a blocked pattern. |
| Unmatched | The value matched neither list. |
The corresponding options then determine whether the request is allowed, recorded, logged, or short-circuited with BlockStatusCode. When a value appears in both lists, FilterPriority controls which result wins.
Option names differ slightly between components, but the recurring controls are:
WhitelistandBlacklistFilterPriorityAllowBlacklistedRequestsAllowUnmatchedRequestsRecordBlacklistedRequestsRecordUnmatchedRequests- per-result log levels
BlockStatusCode
Most middleware components expose these registration forms:
// Bind the conventional XOptions configuration section.
builder.Services.AddHostNameFiltering();
// Bind configuration, then apply code-based overrides.
builder.Services.AddHostNameFiltering(options =>
{
options.Whitelist = new() { "api.example.com" };
options.AllowUnmatchedRequests = false;
});
// Bind from an explicitly supplied configuration root, then override values.
builder.Services.AddHostNameFiltering(
builder.Configuration,
options => options.BlockStatusCode = StatusCodes.Status403Forbidden);Several middleware components also provide UseX(Action<XOptions>). That overload adds a pipeline-local configuration layer on top of the DI-registered options.
Configuration-bound middleware uses options monitoring. Reloadable configuration sources can therefore update values without rebuilding the middleware pipeline.
String-based filters use anchored wildcard patterns rather than raw regular expressions:
| Token | Meaning |
|---|---|
* |
Zero or more characters |
? |
Zero or one character |
# |
Exactly one character |
Examples:
| Pattern | Example match |
|---|---|
*.example.com |
api.example.com |
api-##.example.com |
api-01.example.com |
/v?/health |
/v1/health and /v/health |
Matching is anchored to the complete observed value. Matching is case-insensitive by default where the corresponding options expose a case-sensitivity switch.
using Eigenverft.Routed.RequestFilters.Middleware.HttpMethodFiltering;
builder.Services.AddHttpMethodFiltering(options =>
{
options.Whitelist = new() { "GET", "HEAD" };
options.AllowBlacklistedRequests = false;
options.AllowUnmatchedRequests = false;
options.BlockStatusCode = StatusCodes.Status405MethodNotAllowed;
});Add the matching middleware before the endpoints it protects:
app.UseHttpMethodFiltering();
app.MapControllers();Configure trusted forwarded headers before remote-IP or CIDR filters. The host application remains responsible for defining trusted proxies and networks.
app.UseForwardedHeaders();
app.UseRemoteIpAddressFiltering();
app.UseCidrFiltering();Never accept arbitrary forwarded headers from untrusted networks. Incorrect proxy trust configuration can make client-IP policies ineffective.
using Eigenverft.Routed.RequestFilters.Middleware.FilteringEvaluationGate;
using Eigenverft.Routed.RequestFilters.Middleware.HostNameFiltering;
using Eigenverft.Routed.RequestFilters.Services.FilteringEvaluation.FilteringEvaluators;
using Eigenverft.Routed.RequestFilters.Services.FilteringEvent.FilteringStorage;
builder.Services.AddFilteringEventStorage<InMemoryStorage>();
builder.Services.AddFilteringEvaluator(FilteringEvaluatorKind.SimpleFilteringScore);
builder.Services.AddHostNameFiltering();
builder.Services.AddFilteringEvaluationGate();
var app = builder.Build();
app.UseHostNameFiltering();
app.UseFilteringEvaluationGate();Use NullStorage when events should be discarded, InMemoryStorage for process-local bounded storage, or SqliteStorage when events must survive process restarts. Evaluator and storage selection use a last-call-wins model.
| Component | Registration | Pipeline | Observed value |
|---|---|---|---|
| Accept language | AddAcceptLanguageFiltering |
UseAcceptLanguageFiltering |
Accept-Language header |
| CIDR | AddCidrFiltering |
UseCidrFiltering |
Remote address and CIDR networks |
| File extension | AddFileExtensionBlocking |
UseFileExtensionBlocking |
Requested file extension |
| Host name | AddHostNameFiltering |
UseHostNameFiltering |
Request host name |
| HTTP method | AddHttpMethodFiltering |
UseHttpMethodFiltering |
GET, POST, and other methods |
| HTTP protocol | AddHttpProtocolFiltering |
UseHttpProtocolFiltering |
HTTP protocol/version |
| Path depth | AddPathDepthFiltering |
UsePathDepthFiltering |
Number of path segments |
| Remote IP | AddRemoteIpAddressFiltering |
UseRemoteIpAddressFiltering |
Individual remote IP address |
| Request signature | AddRequestSignatureFiltering |
UseRequestSignatureFiltering |
Composite request signature |
| Request URL | AddRequestUrlFiltering |
UseRequestUrlFiltering |
Complete request URL |
| TLS protocol | AddTlsProtocolFiltering |
UseTlsProtocolFiltering |
Negotiated TLS protocol |
| URI segment | AddUriSegmentFiltering |
UseUriSegmentFiltering |
Individual path segments |
| User agent | AddUserAgentFiltering |
UseUserAgentFiltering |
User-Agent header |
| Component | Registration | Pipeline | Purpose |
|---|---|---|---|
| Browser bootstrap filtering | AddBrowserBootstrapFiltering |
UseBrowserBootstrapFiltering |
Detect and control browser bootstrap requests. |
| Development unlocker | AddDevelopmentUnlocker |
UseDevelopmentUnlocker |
Apply explicitly configured development unlock behavior. |
| Evaluation gate | AddFilteringEvaluationGate |
UseFilteringEvaluationGate |
Enforce a filtering evaluator decision. |
Each component has a dedicated namespace below:
Eigenverft.Routed.RequestFilters.Middleware.<ComponentName>
This keeps imports and registrations explicit and lets consumers include only the components they use.
Individual filters can emit FilteringEvent records. An evaluator can aggregate the events associated with an observed remote address and produce an allow/block decision for FilteringEvaluationGate.
| Kind | Behavior |
|---|---|
NullFiltering |
Always uses the no-op evaluator. |
SimpleFilteringScore |
Evaluates the accumulated filter score. |
SourceAndMatchKindWeighted |
Applies configurable weights by event source and match kind. |
Register exactly one active evaluator:
builder.Services.AddFilteringEvaluator(
FilteringEvaluatorKind.SourceAndMatchKindWeighted);| Selection | Behavior |
|---|---|
NullStorage |
Discards events. |
InMemoryStorage |
Keeps a bounded process-local event history. |
SqliteStorage |
Persists events in SQLite. |
Register exactly one active storage:
builder.Services.AddFilteringEventStorage<SqliteStorage>();Configurable backends bind their conventional sections:
InMemoryFilteringEventStorageOptionsInSqliteDbFilteringEventStorageOptionsSourceAndMatchKindWeightedFilteringEvaluatorOptions
FilteringEvaluationGate can enforce the evaluator result or run in allow-through mode. Allow-through mode is useful for observing a new policy before enabling blocking.
Middleware order is part of the policy. A typical application should consider this sequence:
- Configure trusted forwarded headers when running behind a reverse proxy.
- Apply inexpensive request classifiers and filters.
- Apply
FilteringEvaluationGateafter the filters whose events it evaluates. - Map application endpoints and resources after the filters that should protect them.
A minimal composed pipeline might look like this:
app.UseForwardedHeaders();
app.UseHostNameFiltering();
app.UseUserAgentFiltering();
app.UseFilteringEvaluationGate();
app.MapControllers();Only include middleware that has been registered and configured for the application. Filters that need the peer address insert the shared ClientNetwork feature once; proxy trust and forwarded-header ordering remain the host application's responsibility.
Application hosting, Kestrel/SNI, certificates, configuration-source composition, startup logging, static-file serving, warm-up, health probes, canonical redirects, general request logging, and traffic shaping are intentionally outside this package. Applications that need those capabilities reference their dedicated WebLib or NetLib packages directly; they are not forwarded or transitively exposed by RequestFilters.
git clone https://github.com/eigenverft/Eigenverft.Routed.RequestFilters.git
cd Eigenverft.Routed.RequestFilters
dotnet restore src/Eigenverft.Routed.RequestFilters.slnx
dotnet build src/Eigenverft.Routed.RequestFilters.slnx -c Release --no-restoreCreate a local NuGet package with repository pack metadata enabled:
dotnet pack src/prj/Eigenverft.Routed.RequestFilters/Eigenverft.Routed.RequestFilters.csproj -c Release -p:Stage=packMain source layout:
src/
βββ Eigenverft.Routed.RequestFilters.slnx
βββ prj/
βββ Eigenverft.Routed.RequestFilters/
β βββ Middleware/
β βββ Services/
βββ Eigenverft.Routed.RequestFilters.Tests/
This library provides application middleware, not a complete web application firewall. Review every enabled filter, default, bypass, proxy, and logging setting for the deployment environment.
In particular:
- configure enforcement flags explicitly;
- verify trusted proxies before relying on client-IP information;
- place filters before the endpoints or resources they protect;
- start new evaluator policies in allow-through mode where practical;
- do not expose development unlock behavior in production;
- avoid logging secrets, credentials, or sensitive request data;
- treat preview upgrades as potentially breaking until the project reaches 1.0.
Preview packages are produced by the repository's CI/CD workflow. Source, releases, and issue tracking are available in the GitHub repository.
Licensed under the MIT License by Eigenverft.