Nedo.AspNet.Request.Validation
1.2.0
See the version list below for details.
dotnet add package Nedo.AspNet.Request.Validation --version 1.2.0
NuGet\Install-Package Nedo.AspNet.Request.Validation -Version 1.2.0
<PackageReference Include="Nedo.AspNet.Request.Validation" Version="1.2.0" />
<PackageVersion Include="Nedo.AspNet.Request.Validation" Version="1.2.0" />
<PackageReference Include="Nedo.AspNet.Request.Validation" />
paket add Nedo.AspNet.Request.Validation --version 1.2.0
#r "nuget: Nedo.AspNet.Request.Validation, 1.2.0"
#:package Nedo.AspNet.Request.Validation@1.2.0
#addin nuget:?package=Nedo.AspNet.Request.Validation&version=1.2.0
#tool nuget:?package=Nedo.AspNet.Request.Validation&version=1.2.0
Nedo.AspNet.Request.Validation
An annotation-based request validation library for ASP.NET Core, built to work with Nedo.AspNet.Common.Validation.Core.
This library allows you to validate HTTP request DTOs using attributes, while keeping validation logic centralized, reusable, and independent from controllers.
Features
- Validate request DTOs using attributes
- Produce consistent error codes & messages
- Support multi-language validation messages
- Integrate cleanly with ASP.NET Core + Swagger
Architecture
ASP.NET Core Application
↓
Custom Middleware (Culture / Policy)
↓
IRequestValidationContext
↓
Request.Validation (Attributes)
↓
Common.Validation.Core (Rules)
↓
Localization (.resx)
Important This library does not:
- read HTTP headers automatically
- decide culture
- access database directly
Those are application responsibilities.
Installation
dotnet add package Nedo.AspNet.Request.Validation --version x.x.x
Use the latest version available on NuGet. Or via local NuGet if you are developing the library.
Program.cs Configuration (REQUIRED)
1. Register Request Validation
using Nedo.AspNet.Request.Validation.Extensions;
builder.Services.AddRequestValidation();
2. Disable ASP.NET Core implicit [Required] (IMPORTANT)
ASP.NET Core automatically treats non-nullable reference types as [Required]. This option disables that behavior so all validation is handled explicitly by Nedo.AspNet.Request.Validation.
builder.Services.AddControllers(options =>
{
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
});
Why this matters:
- Prevents ASP.NET Core from auto-injecting
[Required] - Ensures all validation flows through this library
- Keeps error codes & messages consistent
Localization & Language Handling
Default behavior
Validation messages default to English (en).
This comes from the default implementation:
public sealed class RequestValidationContext : IRequestValidationContext
{
public CultureInfo CurrentCulture { get; set; }
public RequestValidationContext()
{
CurrentCulture = new CultureInfo("en");
}
}
Not Automatic
The library does NOT automatically read
Accept-Language.
Culture must be provided by the application.
✅ Custom Culture Middleware (Example)
using System.Globalization;
using Nedo.AspNet.Request.Validation.Abstractions;
public sealed class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IRequestValidationContext validationContext)
{
CultureInfo? culture =
TryGetFromHeader(context) ??
TryGetFromJwt(context);
if (culture is not null)
{
validationContext.CurrentCulture = culture;
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
await _next(context);
}
private CultureInfo? TryGetFromHeader(HttpContext context)
{
var header = context.Request.Headers["Accept-Language"]
.FirstOrDefault()?
.Split(',')[0]
.Split('-')[0]
.Trim();
return TryCreateCulture(header);
}
private CultureInfo? TryGetFromJwt(HttpContext context)
{
if (context.User.Identity?.IsAuthenticated != true)
return null;
return TryCreateCulture(context.User.FindFirst("language")?.Value);
}
private static CultureInfo? TryCreateCulture(string? cultureName)
{
if (string.IsNullOrWhiteSpace(cultureName))
return null;
try
{
return new CultureInfo(cultureName);
}
catch (CultureNotFoundException)
{
return null;
}
}
}
Register it:
app.UseMiddleware<RequestCultureMiddleware>();
Sample Application (Swagger Demo)
A runnable example is provided in:
samples/ConsoleDemo
▶️ How to Run
cd samples/ConsoleDemo
dotnet run
Then open your browser:
http://localhost:xxxx/swagger
To try different validation scenarios, modify the attributes in:
samples/ConsoleDemo/Models/ValidationTestRequest.cs
Sample Request Model
using Nedo.AspNet.Request.Validation.Attributes.Generic;
using Nedo.AspNet.Request.Validation.Attributes.String;
public class ValidationTestRequest
{
[CamelCaseOnly]
[MaxLength(25)]
public string Name { get; set; } = string.Empty;
[Required]
[MaxLength(1)]
public string Email { get; set; } = string.Empty;
[MaxLength(50)]
public string? Description { get; set; }
}
Example Invalid Request (via Swagger)
{
"name": "JOHN_DOE",
"email": ""
}
Example Validation Response
{
"success": false,
"code": "VAL-001",
"message": "Validation failed",
"error": [
{
"errorDetails": {
"fieldErrors": [
{
"field": "Name",
"code": "VAL-GEN-006",
"message": "The string 'Name' must be in camelCase format."
},
{
"field": "Email",
"code": "VAL-GEN-001",
"message": "The field 'Email' is required."
}
]
}
}
]
}
Custom Validators (DI REQUIRED)
Some validators require runtime dependencies and must be registered manually using Dependency Injection.
🔹 CustomHoliday Validator
Signature (Common layer):
Func<int, int, bool>
Usage:
[CustomHoliday]
public DateOnly EventDate { get; set; }
🔹 Unique Validator
Signature (Common layer):
Func<string, bool>
Recommended Structure
YourApp/
├── Extensions/
│ └── ServiceCollectionExtensions.cs
├── Program.cs
Service Collection Extension
Extensions/ServiceCollectionExtensions.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
namespace YourApp.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddValidationDependencies(
this IServiceCollection services)
{
// CustomHoliday validator dependency
services.AddSingleton<Func<int, int, bool>>(_ =>
{
return (month, day) =>
(month == 1 && day == 1) || // New Year
(month == 12 && day == 25); // Christmas
});
// Unique validator dependency
services.AddScoped<Func<string, bool>>(provider =>
{
var db = provider.GetRequiredService<AppDbContext>();
return value => db.Users.Any(u => u.Email == value);
});
return services;
}
}
Notes:
- Func<int, int, bool> → used by CustomHolidayValidator
- Func<string, bool> → used by UniqueValidator
- Business logic stays inside the application, not the library
Program.cs Registration
builder.Services.AddValidationDependencies();
Without this registration → the validator will not work.
Supported Validators
Generic Validators
| Attribute | Validator | Description |
|---|---|---|
[Required] |
RequiredValidator | Field must not be null or empty |
[MinLength(n)] |
MinLengthValidator | Minimum string length |
[MaxLength(n)] |
MaxLengthValidator | Maximum string length |
[ExactLength(n)] |
ExactLengthValidator | String length must match exactly |
String Validators
| Attribute | Validator | Description |
|---|---|---|
[Alpha] |
AlphaValidator | Letters only |
[AlphaNumeric] |
AlphaNumericValidator | Letters and numbers only |
[AlphaSpaceQuote] |
AlphaSpaceQuoteValidator | Letters, spaces, and quotes only |
[CamelCaseOnly] |
CamelCaseValidator | Must follow camelCase format |
[PascalCaseOnly] |
PascalCaseValidator | Must follow PascalCase format |
[SnakeCaseOnly] |
SnakeCaseValidator | Must follow snake_case format |
[KebabCaseOnly] |
KebabCaseValidator | Must follow kebab-case format |
[LowerCaseOnly] |
LowerCaseValidator | Lowercase letters only |
[UpperCaseOnly] |
UpperCaseValidator | Uppercase letters only |
[TitleCaseOnly] |
TitleCaseValidator | Must follow Title Case format |
[NumberOnlyString] |
NumberOnlyStringValidator | Digits only |
[NoWhitespace] |
NoWhitespaceValidator | No whitespace allowed |
[NoSpecialCharacter] |
NoSpecialCharacterValidator | Special characters are not allowed |
[Contains("value")] |
ContainsValidator | Must contain the specified value |
[NotContains("value")] |
NotContainsValidator | Must NOT contain the specified value |
[StartsWith("value")] |
StartsWithValidator | Must start with the specified value |
[EndsWith("value")] |
EndsWithValidator | Must end with the specified value |
Numeric Validators
| Attribute | Validator | Description |
|---|---|---|
[MinValue(n)] |
MinValueValidator | Minimum numeric value |
[MaxValue(n)] |
MaxValueValidator | Maximum numeric value |
[Range(min, max)] |
RangeValidator | Value must be within range |
[PositiveNumber] |
PositiveNumberValidator | Must be greater than zero |
[NegativeNumber] |
NegativeNumberValidator | Must be less than zero |
[NonZero] |
NonZeroValidator | Value must not be zero |
[EvenNumber] |
EvenNumberValidator | Must be an even number |
[OddNumber] |
OddNumberValidator | Must be an odd number |
[WholeNumber] |
WholeNumberValidator | Must be a whole number |
[DivisibleBy(n)] |
DivisibleByValidator | Must be divisible by specified number |
[DecimalPlaces(n)] |
DecimalPlacesValidator | Limit decimal places |
Date (DateOnly) Validators
| Attribute | Validator | Description |
|---|---|---|
[ExactDate] |
ExactDateValidator | Must match exact date |
[DateRange(min, max)] |
DateRangeValidator | Date must be within range |
[MinDate(date)] |
MinDateValidator | Minimum allowed date |
[MaxDate(date)] |
MaxDateValidator | Maximum allowed date |
[PastDate] |
PastDateValidator | Must be in the past |
[FutureDate] |
FutureDateValidator | Must be in the future |
[LeapYear] |
LeapYearValidator | Must be in a leap year |
[Weekday] |
WeekdayValidator | Must be a weekday |
[Weekend] |
WeekendValidator | Must be a weekend |
[CustomHoliday] |
CustomHolidayValidator | Must not fall on a custom holiday (DI required) |
DateTime / Time Validators
| Attribute | Validator | Description |
|---|---|---|
[DateTimeRange] |
DateTimeRangeValidator | DateTime must be within range |
[MinDateTime] |
MinDateTimeValidator | Minimum DateTime |
[MaxDateTime] |
MaxDateTimeValidator | Maximum DateTime |
[PastDateTime] |
PastDateTimeValidator | Must be in the past |
[FutureDateTime] |
FutureDateTimeValidator | Must be in the future |
[LeapYearDateTime] |
LeapYearDateTimeValidator | Must be in leap year |
[WeekdayDateTime] |
WeekdayDateTimeValidator | Must be weekday |
[WeekendDateTime] |
WeekendDateTimeValidator | Must be weekend |
[TimeRange] |
TimeRangeValidator | Time must be within range |
[TimeOnlyRange] |
TimeOnlyRangeValidator | TimeOnly within range |
[MinTime] |
MinTimeValidator | Minimum time |
[MaxTime] |
MaxTimeValidator | Maximum time |
[AMOnly] |
AMOnlyValidator | Morning time only |
[PMOnly] |
PMOnlyValidator | Afternoon/evening only |
File Validators
| Attribute | Validator | Description |
|---|---|---|
[FileType] |
FileTypeValidator | Validate file extension |
[FileMimeType] |
FileMimeTypeValidator | Validate MIME type |
[FileMaxSize] |
FileMaxSizeValidator | Maximum file size |
[FileMinSize] |
FileMinSizeValidator | Minimum file size |
[FileNameLength] |
FileNameLengthValidator | Maximum filename length |
[FileNamePattern] |
FileNamePatternValidator | Filename regex validation |
[FileNullBytes] |
FileNullBytesValidator | Detect null bytes (file corruption) |
Image Validators
| Attribute | Validator | Description |
|---|---|---|
[ImageType] |
ImageTypeValidator | Allowed image types |
[ImageMimeType] |
ImageMimeTypeValidator | Validate image MIME type |
[ImageFileNamePattern] |
ImageFileNamePatternValidator | Validate image filename |
[MaxImageSize] |
MaxImageSizeValidator | Maximum image file size |
[MinImageSize] |
MinImageSizeValidator | Minimum image file size |
[ImageResolution] |
ImageResolutionValidator | Exact image resolution |
[MaxResolution] |
MaxResolutionValidator | Maximum resolution |
[MinResolution] |
MinResolutionValidator | Minimum resolution |
[AspectRatio] |
AspectRatioValidator | Validate image aspect ratio |
[PortraitImage] |
PortraitImageValidator | Height > width |
[SquareImage] |
SquareImageValidator | Width equals height |
[ImageMagicBytes] |
ImageMagicBytesValidator | Validate image file signature |
IP Address Validators
| Attribute | Validator | Description |
|---|---|---|
[IPv4Address] |
IPv4Validator | IPv4 format validation |
[IPv6Address] |
IPv6Validator | IPv6 format validation |
[MixedIPv4IPv6] |
MixedIPValidator | IPv4 or IPv6 |
[PrivateIPAddress] |
PrivateIPValidator | Private IP range |
[PublicIPAddress] |
PublicIPValidator | Public IP only |
[LoopbackIPAddress] |
LoopbackIPValidator | Loopback address |
[MulticastIPAddress] |
MulticastIPValidator | Multicast address |
[BroadcastIPAddress] |
BroadcastIPValidator | Broadcast address |
[AnycastIPAddress] |
AnycastIPValidator | Anycast address |
[ReservedIPAddress] |
ReservedIPValidator | Reserved IP |
[GatewayIPAddress] |
GatewayIPValidator | Gateway address |
[UnicastIPAddress] |
UnicastIPValidator | Unicast address |
[StaticIPAddress] |
StaticIPValidator | Static IP |
[DynamicIPAddress] |
DynamicIPValidator | Dynamic IP |
[ASNIPAddress] |
ASNIPValidator | ASN validation |
[ISPOrganizationIPAddress] |
ISPOrgIPValidator | ISP organization check |
[CountryIPAddress] |
CountryIPValidator | Country-based IP |
[BlacklistedIPAddress] |
BlacklistedIPValidator | Blocklisted IP |
[WhitelistedIPAddress] |
WhitelistedIPValidator | Allowlisted IP |
[IPAddressInRange] |
IPRangeValidator | IP range validation |
[ValidCIDRNotation] |
CIDRValidator | CIDR notation validation |
[ValidSubnetMask] |
SubnetMaskValidator | Subnet mask validation |
[ValidTTLForIPAddress] |
TTLValidator | TTL validation |
[ReverseDNSValidation] |
ReverseDNSValidator | Reverse DNS lookup |
Phone Validators
| Attribute | Validator | Description |
|---|---|---|
[ValidPhoneFormat] |
PhoneFormatValidator | Valid phone number format |
[PhoneCountryCode] |
PhoneCountryCodeValidator | Country code validation |
[ValidPhoneCountryCode] |
ValidPhoneCountryCodeValidator | Valid country code |
Money Validators
| Attribute | Validator | Description |
|---|---|---|
[CurrencyFormat] |
CurrencyFormatValidator | Currency formatting |
[ValidCurrencyCode] |
CurrencyCodeValidator | ISO currency code |
[MinAmountMoney] |
MinAmountMoneyValidator | Minimum amount |
[MaxAmountMoney] |
MaxAmountMoneyValidator | Maximum amount |
[PositiveAmountMoney] |
PositiveAmountValidator | Must be positive |
[NonZeroAmountMoney] |
NonZeroAmountValidator | Must not be zero |
[MoneyDecimalPlaces] |
MoneyDecimalPlacesValidator | Decimal precision |
URL Validators
| Attribute | Validator | Description |
|---|---|---|
[ValidUrlFormat] |
UrlFormatValidator | URL format validation |
[SecureUrl] |
SecureUrlValidator | HTTPS only |
[AccessibleUrl] |
AccessibleUrlValidator | URL accessibility |
[IPBasedUrl] |
IPBasedUrlValidator | IP-based URL |
[DomainSpecificUrl] |
DomainUrlValidator | Domain restriction |
[NoFragmentUrl] |
NoFragmentValidator | Disallow URL fragment |
[PortCheckUrl] |
PortCheckValidator | Validate port |
[UrlPath] |
UrlPathValidator | Path validation |
[UrlQueryParameter] |
UrlQueryValidator | Query parameter validation |
UUID Validators
| Attribute | Validator | Description |
|---|---|---|
[Uuid] |
UuidValidator | Valid UUID |
[UuidV7] |
UuidV7Validator | UUID version 7 |
Error Codes Reference
All error codes are inherited from Common.Validation.Core:
| Category | Code Range | Examples |
|---|---|---|
| Generic | VAL-GEN-001 to VAL-GEN-006 | Required, MaxLength, MinLength |
| String | VAL-STR-001 to VAL-STR-017 | LowerCase, UpperCase, AlphaNumeric |
| Numeric | VAL-NUM-001 to VAL-NUM-011 | MinValue, MaxValue, OddNumber |
| Date/Time | VAL-DAT-001 to VAL-DTM-014 | DateRange, MinTime, MaxDateTime |
| Phone | VAL-PHN-001 to VAL-PHN-003 | ValidPhoneFormat |
| File | VAL-FIL-001 to VAL-FIL-006 | FileType, MaxFileSize |
| Image | VAL-IMA-001 to VAL-IMA-012 | ImageType, MaxResolution |
| IP Address | VAL-IPA-001 to VAL-IPA-026 | IPv4, IPv6, PrivateIP |
| URL | VAL-URL-001 to VAL-URL-009 | ValidURL, SecureURL |
| Location | VAL-LOC-001 to VAL-LOC-003 | LatitudeRange, LongitudeRange |
| Money | VAL-MON-001 to VAL-MON-007 | MinAmount, CurrencyFormat |
See Common.Validation README for complete list.
Dependencies
- Nedo.AspNet.Common.Validation.Core (>= 1.0.0) - Core validation logic
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0) - DI support
- .NET 9.0 - Target framework
Project Structure
Nedo.AspNet.Request.Validation/
├── src/
│ └── Nedo.AspNet.Request.Validation/
│ ├── Abstractions/ # Core abstractions (interfaces)
│ ├── Attributes/ # Validation attributes
│ │ ├── Base/ # Base attribute classes
│ │ ├── Generic/ # Required, Length, Regex
│ │ ├── String/ # String format rules
│ │ ├── Numeric/ # Numeric validation
│ │ ├── Date/ # DateOnly validators
│ │ ├── DateTimeValidation/# DateTime & TimeOnly validators
│ │ ├── File/ # File upload validators
│ │ ├── Image/ # Image-specific validators
│ │ ├── IP/ # IP address validators
│ │ ├── Phone/ # Phone number validators
│ │ ├── Money/ # Currency & money validators
│ │ ├── URL/ # URL validators
│ │ └── Uuid/ # UUID validators
│ ├── Context/ # RequestValidationContext
│ ├── Extensions/ # DI & service extensions
│ ├── Filters/ # Validation filters
│ └── bin/ # Build output
├── tests/
│ └── Nedo.AspNet.Request.Validation.Tests/
│ └── Attributes/ # Attribute unit tests
└── samples/
└── ConsoleDemo/ # Runnable Swagger demo
Versioning & Release
This project uses git tags to control versioning. The GitLab CI/CD pipeline automatically builds and publishes NuGet packages when a tag is pushed.
How It Works
- The pipeline triggers only on tags (e.g.
v1.1.1) - The
vprefix is stripped to get the version number (1.1.1) - The package is built, packed, and published to NuGet.org
How to Release a New Version
# 1. Commit and push your changes
git add .
git commit -m "feat: your changes"
git push origin main
# 2. Create a version tag
git tag -a v1.7.2 -m "Release v1.7.2
Added:
- README included in NuGet package for better package description
- Documentation: guide for pushing package to NuGet
Changed:
- Packaging configuration to embed README file
Notes:
No breaking changes. Existing consumers are not affected."
# 3. Push the tag (this triggers the CI/CD pipeline)
git push origin v1.7.2
Version Format
| Type | Example Tag | NuGet Version |
|---|---|---|
| Patch | v1.0.1 |
1.0.1 |
| Minor | v1.1.0 |
1.1.0 |
| Major | v2.0.0 |
2.0.0 |
| Prerelease | v1.1.0-beta.1 |
1.1.0-beta.1 |
The tag must start with v (e.g. v1.1.1, not 1.1.1).
Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/YourFeature) - Commit your changes (
git commit -m "Add awesome feature") - Push to your branch (
git push origin feature/YourFeature) - Open a pull request
Please ensure:
- All tests pass (
dotnet test) - Code follows project conventions
- Add unit tests for new validators
License
This project is licensed under the MIT License.
| 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
- Microsoft.AspNetCore.Http.Abstractions (>= 2.3.9)
- Nedo.AspNet.Common.Validation.Core (>= 1.9.0)
- SixLabors.ImageSharp (>= 3.1.12)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Nedo.AspNet.Request.Validation:
| Package | Downloads |
|---|---|
|
Nedo.AspNet.ApiContracts
Standardized API request and response contracts for ASP.NET applications with snake_case JSON, structured validation errors, Swagger integration, and query engine support (filtering, sorting, pagination). |
GitHub repositories
This package is not used by any popular GitHub repositories.