Nedo.AspNet.Common.Validation.Core
Core validation engine for .NET 9 with 120+ built-in validators across 12 categories, full localization support, and a composable IValidator<T> pipeline.
Install
dotnet add package Nedo.AspNet.Common.Validation.Core
Key Components
| Component |
Description |
IValidator<T> |
Standard interface — implement once, compose everywhere |
ValidationResult |
Aggregated result with .IsValid and .Errors |
ValidationError |
Error detail: Code, Message, MemberNames |
CompositeValidator<T> |
Compose multiple IValidator<T> + inline delegates |
ValidationResultExtensions |
Merge() and AddIfNotNull() helpers |
Quick Start
Static Validator Calls
Each validator is a static method returning ValidationError? — null means the value is valid.
using Nedo.AspNet.Common.Validation.Core.Validators.Generic;
using Nedo.AspNet.Common.Validation.Core.Validators.String;
using Nedo.AspNet.Common.Validation.Core.Validators.Numeric;
// Single check
var error = MinLengthValidator.Validate(
value: userName,
fieldName: "Name",
minLength: 5,
cultureName: "en");
if (error != null)
Console.WriteLine($"[{error.Code}] {error.Message}");
Collecting Multiple Errors
using Nedo.AspNet.Common.Validation.Core;
using Nedo.AspNet.Common.Validation.Core.Contracts;
var errors = new List<ValidationError>();
errors.AddIfNotNull(RequiredValidator.Validate(user.Name, "Name", "en"));
errors.AddIfNotNull(MinLengthValidator.Validate(user.Name, "Name", 5, "en"));
errors.AddIfNotNull(AlphaValidator.Validate(user.Name, "Name", "en"));
errors.AddIfNotNull(RequiredValidator.Validate(user.Email, "Email", "en"));
var result = errors.Count == 0
? ValidationResult.Success
: new ValidationResult(errors);
CompositeValidator
Compose multiple IValidator<T> instances and/or inline delegates into one:
using Nedo.AspNet.Common.Validation.Core;
var composite = new CompositeValidator<UserDto>()
.Add(existingValidator) // Any IValidator<UserDto>
.Add((dto, culture) => // Inline rule
RequiredValidator.Validate(dto.Name, "Name", culture ?? "en"))
.Add((dto, culture) =>
MinLengthValidator.Validate(dto.Name, "Name", 3, culture ?? "en"));
var result = composite.Validate(dto, "id");
Merging Results
var merged = ValidationResultExtensions.Merge(result1, result2, result3);
Localization
All validators accept a cultureName parameter. Built-in .resx files provide messages in:
- English (
en) — default
- Indonesian (
id)
Adding a New Language
- Create
ValidationMessages.<lang>.resx (e.g. ValidationMessages.fr.resx)
- Provide translated values keyed by message name
- Deploy the satellite assembly alongside the library
// Messages are automatically resolved by cultureName parameter
var error = RequiredValidator.Validate(null, "Name", "id");
// → "Kolom Name wajib diisi."
var errorEn = RequiredValidator.Validate(null, "Name", "en");
// → "The field Name is required."
Validator Catalog
Generic Validators
| Code |
Name |
Description |
VAL-GEN-001 |
Required |
Field is not null, empty, or missing |
VAL-GEN-002 |
Min Length |
Minimum character length |
VAL-GEN-003 |
Max Length |
Maximum character length |
VAL-GEN-004 |
Exact Length |
Exact character length |
VAL-GEN-005 |
Unique |
No duplicate values in context |
VAL-GEN-006 |
Regex |
Matches a regular expression pattern |
String Validators
| Code |
Name |
Description |
VAL-STR-001 |
No Whitespace |
No spaces, tabs, etc. |
VAL-STR-002 |
Number Only String |
Only digits (0–9) |
VAL-STR-003 |
Starts With |
Begins with specified prefix |
VAL-STR-004 |
Ends With |
Ends with specified suffix |
VAL-STR-005 |
Contains |
Includes specified substring |
VAL-STR-006 |
Not Contains |
Excludes forbidden substring |
VAL-STR-007 |
Alpha |
Only A–Z, a–z |
VAL-STR-008 |
No Special Character |
No symbols or punctuation |
VAL-STR-009 |
Alpha Space Quote |
Letters, spaces, or ' only |
VAL-STR-010 |
Alpha Numeric |
Letters and numbers only |
VAL-STR-011 |
Upper Case Only |
All uppercase |
VAL-STR-012 |
Lower Case Only |
All lowercase |
VAL-STR-013 |
Title Case Only |
Each word capitalized |
VAL-STR-014 |
Snake Case Only |
snake_case format |
VAL-STR-015 |
Kebab Case Only |
kebab-case format |
VAL-STR-016 |
Camel Case Only |
camelCase format |
VAL-STR-017 |
Pascal Case Only |
PascalCase format |
Numeric Validators
| Code |
Name |
Description |
VAL-NUM-001 |
Minimum Value |
≥ specified minimum |
VAL-NUM-002 |
Maximum Value |
≤ specified maximum |
VAL-NUM-003 |
Range |
Within [min, max] |
VAL-NUM-004 |
Decimal Places |
Max decimal places allowed |
VAL-NUM-005 |
Positive Number |
> 0 |
VAL-NUM-006 |
Negative Number |
< 0 |
VAL-NUM-007 |
Whole Number |
Integer, no decimals |
VAL-NUM-008 |
Non Zero |
≠ 0 |
VAL-NUM-009 |
Even Number |
Divisible by 2 |
VAL-NUM-010 |
Odd Number |
Not divisible by 2 |
VAL-NUM-011 |
Divisible By |
Divisible by specified divisor |
Date Validators
| Code |
Name |
Description |
VAL-DAT-001 |
Date Range |
Between min and max date |
VAL-DAT-002 |
Minimum Date |
Not before minimum |
VAL-DAT-003 |
Maximum Date |
Not after maximum |
VAL-DAT-004 |
Exact Date |
Exact match |
VAL-DAT-005 |
Date Format |
String format check |
VAL-DAT-006 |
Future Date |
After today |
VAL-DAT-007 |
Past Date |
Before today |
VAL-DAT-008 |
Weekend |
Saturday or Sunday |
VAL-DAT-009 |
Weekday |
Monday–Friday |
VAL-DAT-039 |
Leap Year |
Leap year check |
VAL-DAT-040 |
Custom Holiday |
Callback-based holiday check |
DateTime / Time Validators
| Code |
Name |
Description |
VAL-DTM-001 |
DateTime Range |
Within date-time range |
VAL-DTM-002 |
Minimum DateTime |
Not before minimum |
VAL-DTM-003 |
Maximum DateTime |
Not after maximum |
VAL-DTM-004 |
Future DateTime |
After current datetime |
VAL-DTM-005 |
Past DateTime |
Before current datetime |
VAL-DTM-006 |
Time Only Range |
Time within range (ignoring date) |
VAL-DTM-007 |
Weekend DateTime |
Falls on Saturday/Sunday |
VAL-DTM-008 |
Weekday DateTime |
Monday through Friday |
VAL-DTM-009 |
Leap Year DateTime |
Year is a leap year |
VAL-DTM-010 |
Maximum Time |
Time not after max |
VAL-DTM-011 |
Minimum Time |
Time not before min |
VAL-DTM-012 |
Time Range |
Time within daily range |
VAL-DTM-013 |
PM Only |
12:00 PM – 11:59 PM |
VAL-DTM-014 |
AM Only |
12:00 AM – 11:59 AM |
File Validators
| Code |
Name |
Description |
VAL-FIL-001 |
File Type |
Allowed file types (PDF, JPG, etc.) |
VAL-FIL-002 |
Maximum File Size |
Max size in bytes/MB |
VAL-FIL-003 |
Minimum File Size |
Min size in bytes/KB |
VAL-FIL-004 |
MIME Type |
Accepted MIME types |
VAL-FIL-005 |
File Name Pattern |
Name matches pattern |
VAL-FIL-006 |
File Name Length |
Max file name length |
Image Validators
| Code |
Name |
Description |
VAL-IMA-001 |
Image Type |
Allowed image formats |
VAL-IMA-002 |
Maximum Image Size |
Max image file size |
VAL-IMA-003 |
Minimum Image Size |
Min image file size |
VAL-IMA-004 |
Maximum Resolution |
Max width × height |
VAL-IMA-005 |
Minimum Resolution |
Min width × height |
VAL-IMA-006 |
Image Resolution |
Exact required resolution |
VAL-IMA-007 |
Aspect Ratio |
Required aspect ratio |
VAL-IMA-008 |
Image MIME Type |
Accepted image MIME types |
VAL-IMA-009 |
Exact Dimensions |
Exact width × height pixels |
VAL-IMA-010 |
Square Image |
Width equals height |
VAL-IMA-011 |
Portrait Image |
Height > width |
VAL-IMA-012 |
Image File Name |
File name pattern check |
IP Address Validators
| Code |
Name |
Description |
VAL-IPA-001 |
Private IP |
RFC1918 private ranges |
VAL-IPA-002 |
Public IP |
Publicly routable |
VAL-IPA-003 |
IPv4 Only |
Valid IPv4 format |
VAL-IPA-004 |
IPv6 Only |
Valid IPv6 format |
VAL-IPA-005 |
Reserved IP |
IANA reserved blocks |
VAL-IPA-006 |
Loopback |
127.0.0.1, ::1 |
VAL-IPA-007 |
Multicast |
Multicast range |
VAL-IPA-008 |
Unicast |
Standard unicast |
VAL-IPA-009 |
Anycast |
Anycast addressing |
VAL-IPA-010 |
CIDR Notation |
Valid CIDR (e.g. /24) |
VAL-IPA-011 |
IP in Range |
Within configured range |
VAL-IPA-012 |
Subnet Mask |
Valid subnet mask |
VAL-IPA-013 |
Broadcast IP |
Broadcast address |
VAL-IPA-014 |
Gateway IP |
Valid gateway address |
VAL-IPA-015 |
Country-Specific |
Country/region allocation |
VAL-IPA-018 |
ISP/Organization |
ISP or org range |
VAL-IPA-019 |
Blacklisted IP |
Not on blocklist |
VAL-IPA-020 |
Whitelisted IP |
On allowlist |
VAL-IPA-021 |
Dynamic IP |
DHCP-assigned |
VAL-IPA-022 |
Static IP |
Fixed/manual assignment |
VAL-IPA-023 |
Mixed IPv4/IPv6 |
Dual-stack validation |
VAL-IPA-024 |
ASN Validation |
Matches required ASN |
VAL-IPA-025 |
Valid TTL |
TTL validity check |
VAL-IPA-026 |
Reverse DNS |
PTR record validation |
URL Validators
| Code |
Name |
Description |
VAL-URL-001 |
Valid URL |
Syntactically valid URL |
VAL-URL-002 |
Domain Specific |
Matches required domain |
VAL-URL-003 |
Secure URL |
HTTPS required |
VAL-URL-004 |
Port Check |
Includes specific port |
VAL-URL-005 |
IP Based URL |
No raw IP addresses |
VAL-URL-006 |
Query Parameters |
Required query params |
VAL-URL-007 |
No Fragment |
No #fragment |
VAL-URL-008 |
Specific Path |
Includes required path |
VAL-URL-009 |
Accessible URL |
Network reachability check |
UUID Validators
| Code |
Name |
Description |
VAL-UID-001 |
UUID |
Valid UUID/GUID format |
VAL-UID-002 |
UUIDv7 |
RFC 4122 UUID version 7 |
Phone Validators
| Code |
Name |
Description |
VAL-PHN-001 |
Phone Format |
Valid phone number format |
VAL-PHN-002 |
Country Code Required |
Includes country code prefix |
VAL-PHN-003 |
Valid Country Code |
Recognized international code |
Location Validators
| Code |
Name |
Description |
VAL-LOC-001 |
Latitude Range |
Within -90 to +90 |
VAL-LOC-002 |
Longitude Range |
Within -180 to +180 |
VAL-LOC-003 |
Decimal Places |
Minimum coordinate precision |
Money Validators
| Code |
Name |
Description |
VAL-MON-001 |
Minimum Amount |
Not below minimum |
VAL-MON-002 |
Maximum Amount |
Not above maximum |
VAL-MON-003 |
Currency Format |
Valid formatting |
VAL-MON-004 |
Currency Code |
Recognized code (USD, EUR, IDR) |
VAL-MON-005 |
Decimal Places |
Max decimal places |
VAL-MON-006 |
Positive Amount |
> 0 |
VAL-MON-007 |
Non Zero Amount |
≠ 0 |
Validator Method Signature
All validators follow the same pattern:
public static ValidationError? Validate(
<TValue> value, // The value to validate
string fieldName, // Member name for error reporting
<TParam> param, // Optional: validator-specific parameter (minLength, pattern, etc.)
string cultureName) // Language for error messages ("en", "id", etc.)
- Returns
null if valid
- Returns
ValidationError if invalid
- Returns
null for null values (except RequiredValidator) — delegates null checks to RequiredValidator
- Nedo.AspNet.Common.Validation.Fluent — Fluent
AbstractValidator<T> API with chained rules
- Nedo.AspNet.Common.Validation.Dsl — Text-based DSL for runtime rule definitions