Nedo.AspNet.Request.InputValidation
1.0.1
dotnet add package Nedo.AspNet.Request.InputValidation --version 1.0.1
NuGet\Install-Package Nedo.AspNet.Request.InputValidation -Version 1.0.1
<PackageReference Include="Nedo.AspNet.Request.InputValidation" Version="1.0.1" />
<PackageVersion Include="Nedo.AspNet.Request.InputValidation" Version="1.0.1" />
<PackageReference Include="Nedo.AspNet.Request.InputValidation" />
paket add Nedo.AspNet.Request.InputValidation --version 1.0.1
#r "nuget: Nedo.AspNet.Request.InputValidation, 1.0.1"
#:package Nedo.AspNet.Request.InputValidation@1.0.1
#addin nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.1
#tool nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.1
Nedo.AspNet.Request.InputValidation
Middleware-level input validation library for ASP.NET Core. Validates the format, structure, and safety of HTTP requests before model binding — catching threats that field-level validation ([Required], [Range], etc.) cannot.
Table of Contents
- Why This Library?
- Where It Fits
- Installation
- Quick Start
- Configuration
- Configuration Options Reference
- Validators
- Error Codes
- Error Response Format
- Excluding Paths
- Custom Validators
- Relationship with Field-Level Validation
Why This Library?
Field-level validation ([Required], [EmailAddress], FluentValidation) validates property values after model binding. But it cannot protect against:
| Threat | Example |
|---|---|
| Invalid Content-Type | Sending text/xml to a JSON endpoint |
| Malformed JSON | { invalid json crashing the deserializer |
| Oversized Payloads | 100MB body on a simple form endpoint |
| Deep Nesting Attacks | Deeply nested JSON causing stack overflow |
| Invalid MIME Types | Malformed Content-Type header syntax |
Nedo.AspNet.Request.InputValidation fills this gap by validating at the HTTP pipeline level, before model binding occurs.
Where It Fits
HTTP Request
│
▼
┌──────────────────────────────────────┐
│ 🛡️ Request Input Validation │ ◄── THIS LIBRARY
│ (Content-Type, JSON, Size, Format) │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ 📦 Model Binding & Deserialization │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ ✅ Field-Level Validation │ ◄── [Required], [Range], etc.
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ 🎯 Controller / Endpoint Logic │
└──────────────────────────────────────┘
Installation
dotnet add package Nedo.AspNet.Request.InputValidation
Quick Start
1. Register Services
using Nedo.AspNet.Request.InputValidation;
var builder = WebApplication.CreateBuilder(args);
// Option A: Using appsettings.json
builder.Services.AddRequestInputValidation(
builder.Configuration.GetSection("NedoRequestInputValidation")
);
// Option B: Using code
builder.Services.AddRequestInputValidation(options =>
{
options.EnableFormatValidators = true;
options.MaxRequestBodySize = 10_000_000; // 10MB
options.MaxJsonDepth = 32;
options.AllowedContentTypes.Add("application/json");
});
2. Enable Middleware
Add the middleware to the pipeline before your endpoints:
var app = builder.Build();
app.UseRequestInputValidation(); // Add before MapControllers / MapEndpoints
app.MapControllers();
app.Run();
Configuration
Using appsettings.json (Recommended)
Add the NedoRequestInputValidation section to your appsettings.json:
{
"NedoRequestInputValidation": {
"EnableFormatValidators": true,
"MaxJsonDepth": 32,
"MaxRequestBodySize": 10485760,
"AllowedContentTypes": [
"application/json",
"multipart/form-data"
],
"ExcludedPaths": [
"/hub",
"/signalr"
]
}
}
Then bind it in Program.cs:
builder.Services.AddRequestInputValidation(
builder.Configuration.GetSection("NedoRequestInputValidation")
);
Note: When using
appsettings.json, theAllowedContentTypeslist is exactly what you define — there are no hidden defaults. If you don't configure it, all content types are allowed.
Using Code
builder.Services.AddRequestInputValidation(options =>
{
options.EnableFormatValidators = true;
options.MaxRequestBodySize = 5_000_000;
options.MaxJsonDepth = 10;
options.AllowedContentTypes.Add("application/json");
options.AllowedContentTypes.Add("multipart/form-data");
options.ExcludedPaths.Add("/hub");
});
Configuration Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
EnableSecurityValidators |
bool |
true |
Enable security validators (SQL Injection, XSS, etc.) — reserved for future phases |
EnableContentValidators |
bool |
true |
Enable content validators (Required fields, etc.) — reserved for future phases |
EnableEncodingValidators |
bool |
true |
Enable encoding validators (UTF-8, binary rejection) — reserved for future phases |
EnableFormatValidators |
bool |
true |
Enable format validators (Content-Type, JSON syntax, body size) |
MaxJsonDepth |
int |
32 |
Maximum allowed JSON nesting depth |
MaxRequestBodySize |
long |
30,000,000 |
Maximum request body size in bytes (~30MB) |
AllowedContentTypes |
List<string> |
[] (empty) |
Allowed Content-Type values. Empty = allow all |
ExcludedPaths |
List<string> |
[] (empty) |
Request paths to skip validation (prefix match, case-insensitive) |
Validators
ContentTypeValidator
Order: 10 | Error Codes: INV-FMT-001, INV-FMT-002
Validates the Content-Type header of incoming requests.
- Skips validation when request has no body (
ContentLengthis 0 or null) - Skips validation when
AllowedContentTypesis empty (not configured) - Handles charset parameters (e.g.,
application/json; charset=utf-8→ checksapplication/json)
Examples:
✅ POST /api/data Content-Type: application/json → Passes (if "application/json" is allowed)
✅ POST /api/data Content-Type: application/json; charset=utf-8 → Passes
❌ POST /api/data Content-Type: text/xml → INV-FMT-002 (not in allowed list)
❌ POST /api/data (no Content-Type header) → INV-FMT-001 (missing)
MediaTypeValidator
Order: 11 | Error Code: INV-FMT-003
Validates that the Content-Type header is a syntactically valid MIME type (RFC compliant).
- Skips validation when Content-Type is not present
- Uses
MediaTypeHeaderValue.TryParse()for RFC-compliant parsing
Examples:
✅ Content-Type: application/json → Passes (valid syntax)
✅ Content-Type: multipart/form-data → Passes
❌ Content-Type: invalid-format/ → INV-FMT-003 (invalid MIME syntax)
InputStreamValidator
Order: 5 | Error Code: INV-FMT-004
Validates the size of the request body based on the Content-Length header. Runs early in the pipeline to reject oversized payloads before any processing.
Examples:
✅ POST /api/data Content-Length: 1024 (MaxRequestBodySize: 10MB) → Passes
❌ POST /api/data Content-Length: 50MB (MaxRequestBodySize: 10MB) → INV-FMT-004
JsonFormatValidator
Order: 20 | Error Codes: INV-FMT-005, INV-FMT-006
Validates that the request body contains valid JSON syntax before model binding attempts deserialization. Also enforces maximum nesting depth.
- Only validates requests with
Content-Type: application/json - Enables request body buffering so the stream can be re-read by model binding
- Resets the stream position after validation
Examples:
✅ {"name": "John", "age": 30} → Passes
❌ {"name": "John", "age": → INV-FMT-005 (invalid JSON)
❌ {"a":{"b":{"c":{"d":1}}}} (MaxDepth: 2) → INV-FMT-005 (depth exceeded)
Error Codes
| Code | Validator | Description |
|---|---|---|
INV-FMT-001 |
ContentTypeValidator | Content-Type header is missing |
INV-FMT-002 |
ContentTypeValidator | Content-Type is not in the allowed list |
INV-FMT-003 |
MediaTypeValidator | Content-Type has invalid MIME type syntax |
INV-FMT-004 |
InputStreamValidator | Request body exceeds maximum size |
INV-FMT-005 |
JsonFormatValidator | Invalid JSON syntax or depth exceeded |
INV-FMT-006 |
JsonFormatValidator | Unable to read request body |
Error Response Format
When validation fails, the middleware returns 400 Bad Request with a structured JSON response:
{
"status": 400,
"title": "Input Validation Failed",
"errors": [
{
"code": "INV-FMT-002",
"message": "Content-Type 'text/xml' is not allowed.",
"field": "Header:Content-Type",
"category": "InputFormat"
}
]
}
Multiple errors can be returned in a single response if multiple validators detect issues.
Excluding Paths
Use ExcludedPaths to skip validation for specific routes. This is useful for:
- SignalR hubs (
/hub,/signalr) — WebSocket negotiation has different content patterns - Health checks (
/health,/ready) - Static files or other non-API endpoints
{
"NedoRequestInputValidation": {
"ExcludedPaths": [
"/hub",
"/signalr",
"/health"
]
}
}
Path matching is prefix-based and case-insensitive:
/hubmatches/hub,/hub/negotiate,/hub/stream, etc./api/internalmatches/api/internal/status,/api/internal/debug, etc.
Custom Validators
You can create your own validators by implementing IRequestInputValidator:
using Microsoft.AspNetCore.Http;
using Nedo.AspNet.Request.InputValidation.Contracts;
public class CustomHeaderValidator : IRequestInputValidator
{
public int Order => 15; // Controls execution order (lower = first)
public Task<InputValidationResult> ValidateAsync(HttpContext context)
{
if (!context.Request.Headers.ContainsKey("X-Api-Key"))
{
return Task.FromResult(InputValidationResult.Failure(new InputValidationError
{
Code = "INV-CUS-001",
Message = "X-Api-Key header is required.",
Field = "Header:X-Api-Key",
Category = "Custom"
}));
}
return Task.FromResult(InputValidationResult.Success);
}
}
Register it in DI:
builder.Services.AddSingleton<IRequestInputValidator, CustomHeaderValidator>();
Relationship with Field-Level Validation
| Aspect | Field-Level Validation | Request Input Validation (This Library) |
|---|---|---|
| Level | Property level | HTTP Request / Pipeline level |
| When | After model binding | Before model binding |
| What | Property values (string, int, DateTime) |
Raw request (headers, body stream) |
| Focus | Correctness (format, range, pattern) | Safety (format, size, structure) |
| Integration | Attributes, FluentValidation | Middleware |
| Error Codes | VAL-xxx-xxx |
INV-xxx-xxx |
Use both together for a complete validation pipeline:
Request → [Input Validation] → [Model Binding] → [Field Validation] → Controller
🛡️ Safety 📦 Parse ✅ Correctness 🎯 Logic
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.