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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nedo.AspNet.Request.InputValidation" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nedo.AspNet.Request.InputValidation" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Nedo.AspNet.Request.InputValidation" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nedo.AspNet.Request.InputValidation --version 1.0.1
                    
#r "nuget: Nedo.AspNet.Request.InputValidation, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nedo.AspNet.Request.InputValidation@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.1
                    
Install as a Cake Tool

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?

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

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, the AllowedContentTypes list 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 (ContentLength is 0 or null)
  • Skips validation when AllowedContentTypes is empty (not configured)
  • Handles charset parameters (e.g., application/json; charset=utf-8 → checks application/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:

  • /hub matches /hub, /hub/negotiate, /hub/stream, etc.
  • /api/internal matches /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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.

Version Downloads Last Updated
1.0.1 35 2/13/2026
1.0.0 39 2/13/2026