Validus 3.1.0
See the version list below for details.
dotnet add package Validus --version 3.1.0
NuGet\Install-Package Validus -Version 3.1.0
<PackageReference Include="Validus" Version="3.1.0" />
paket add Validus --version 3.1.0
#r "nuget: Validus, 3.1.0"
// Install Validus as a Cake Addin #addin nuget:?package=Validus&version=3.1.0 // Install Validus as a Cake Tool #tool nuget:?package=Validus&version=3.1.0
Validus
Validus is a composable validation library for F#, with built-in validators for most primitive types and easily extended through custom validators.
Key Features
- Composable validation.
- Built-in validators for most primitive types.
- Easily extended through custom-validators.
- Infix operators to provide clean syntax or.
- Applicative computation expression (
validate { ... }
). - Excellent for creating constrained-primitives (i.e., value objects).
Quick Start
A common example of receiving input from an untrusted source PersonDto
(i.e., HTML form submission), applying validation and producing a result based on success/failure.
open System
open Validus
open Validus.Operators
type PersonDto =
{ FirstName : string
LastName : string
Email : string
Age : int option
StartDate : DateTime option }
type Name =
{ First : string
Last : string }
type Person =
{ Name : Name
Email : string
Age : int option
StartDate : DateTime }
let validatePersonDto (input : PersonDto) : Result<Person, ValidationErrors> =
// Shared validator for first & last name
let nameValidator =
Validators.Default.String.betweenLen 3 64
// Composing multiple validators to form complex validation rules,
// overriding default error message (Note: "Validators.String" as
// opposed to "Validators.Default.String")
let emailValidator =
Validators.Default.String.betweenLen 8 512
<+> Validators.String.pattern "[^@]+@[^\.]+\..+" (sprintf "Please provide a valid %s")
// Defining a validator for an option value
let ageValidator =
Validators.optional (Validators.Default.Int.between 1 100)
// Defining a validator for an option value that is required
let dateValidator =
Validators.Default.required (Validators.Default.DateTime.greaterThan DateTime.Now)
validate {
let! first = nameValidator "First name" input.FirstName
and! last = nameValidator "Last name" input.LastName
and! email = emailValidator "Email address" input.Email
and! age = ageValidator "Age" input.Age
and! startDate = dateValidator "Start Date" input.StartDate
// Construct Person if all validators return Success
return {
Name = { First = first; Last = last }
Email = email
Age = age
StartDate = startDate }
}
And, using the validator:
let input : PersonDto =
{ FirstName = "John"
LastName = "Doe"
Email = "john.doe@url.com"
Age = Some 63
StartDate = Some (new DateTime(2058, 1, 1)) }
match validatePersonDto input with
| Success p -> printfn "%A" p
| Failure e ->
e
|> ValidationErrors.toList
|> Seq.iter (printfn "%s")
Custom Validators
Custom validators can be created by combining built-in validators together using Validator.compose
, or the <+>
infix operator, as well as creating bespoke validator's using Validator.create
.
Combining built-in validators
open Validus
open Validus.Operators
let emailValidator =
Validators.Default.String.betweenLen 8 512
<+> Validators.String.pattern "[^@]+@[^\.]+\..+" (sprintf "%s must be a valid email")
"fake@test"
|> emailValidator "Login email"
// Outputs: [ "Login email", [ "Login email must be a valid email" ] ]
Note: This is for demo purposes only, it likely isn't advisable to attempt to validate emails using a regular expression. Instead, use System.Net.MailAddress.
Creating a bespoke validator
open Validus
let fooValidator =
let fooRule v = v = "foo"
let fooMessage = sprintf "%s must be a string that matches 'foo'"
Validator.create fooMessage fooRule
"bar"
|> fooValidator "Test string"
// Outputs: [ "Test string", [ "Test string must be a string that matches 'foo'" ] ]
Validating Collections
Applying validator(s) to a set of items will result in a Result<'a, ValidationErrors> seq
open Validus
open Validus.Operators
let emailValidator =
Validators.Default.String.betweenLen 8 512
<+> Validators.String.pattern "[^@]+@[^\.]+\..+" (sprintf "%s must be a valid email")
let emails = [ "fake@test"; "bob@fsharp.org"; "x" ]
let result =
emails
|> List.map (emailValidator "Login email")
// result is a Result<string, ValidationErrors> seq
// Outputs: [ "Login email", [ "Login email must be a valid email" ] ]
Constrained Primitives (i.e., value types/objects)
It is generally a good idea to create value objects to represent individual data points that are more classified than the primitive types usually used to represent them.
Example 1: Email Address Value Object
A good example of this is an email address being represented as a string
literal, as it exists in many programs. This is however a flawed approach in that the domain of an email address is more tightly scoped than a string will allow. For example, ""
or null
are not valid emails.
To address this, we can create a wrapper type to represent the email address which hides away the implementation details and provides a smart construct to produce the type.
type Email =
private { Email : string }
override x.ToString () = x.Email
static member Of field input =
let rule (x : string) =
if x = "" then false
else
try
let addr = MailAddress(x)
if addr.Address = x then true
else false
with
| :? FormatException -> false
let message = sprintf "%s must be a valid email address"
input
|> Validator.create message rule field
|> Result.map (fun v -> { Email = v })
Example 2: E164 Formatted Phone Number
type E164 =
private { E164 : string }
override x.ToString() = x.E164
static member Of field input =
let e164Regex = @"^\+[1-9]\d{1,14}$"
let message = sprintf "%s must be a valid E164 telephone number"
input
|> Validators.String.pattern e164Regex message field
|> Result.map (fun v -> { E164 = v })
Built-in Validators
All of the built-in validators reside in the Validators
module and follow a similar definition.
// Produce a validation result based on a field name and value
string -> 'a -> Result<'a, ValidationErrors>
Note: Validators pre-populated with English-language default error messages reside within the
Validators.Default
module.
equals
Applies to: string, int16, int, int64, decimal, float, DateTime, DateTimeOffset, TimeSpan
open Validus
// Define a validator which checks if a string equals
// "foo" displaying the standard error message.
let equalsFoo =
Validators.Default.String.equals "foo" "fieldName"
equalsFoo "bar" // Result<string, ValidationErrors>
// Define a validator which checks if a string equals
// "foo" displaying a custom error message (string -> string).
let equalsFooCustom =
Validators.String.equals "foo" (sprintf "%s must equal the word 'foo'") "fieldName"
equalsFooCustom "bar" // Result<string, ValidationErrors>
notEquals
Applies to: string, int16, int, int64, decimal, float, DateTime, DateTimeOffset, TimeSpan
open Validus
// Define a validator which checks if a string is not
// equal to "foo" displaying the standard error message.
let notEqualsFoo =
Validators.Default.String.notEquals "foo" "fieldName"
notEqualsFoo "bar" // Result<string, ValidationErrors>
// Define a validator which checks if a string is not
// equal to "foo" displaying a custom error message (string -> string)
let notEqualsFooCustom =
Validators.String.notEquals "foo" (sprintf "%s must not equal the word 'foo'") "fieldName"
notEqualsFooCustom "bar" // Result<string, ValidationErrors>
between
(inclusive)
Applies to: int16, int, int64, decimal, float, DateTime, DateTimeOffset, TimeSpan
open Validus
// Define a validator which checks if an int is between
// 1 and 100 (inclusive) displaying the standard error message.
let between1and100 =
Validators.Default.Int.between 1 100 "fieldName"
between1and100 12 // Result<int, ValidationErrors>
// Define a validator which checks if an int is between
// 1 and 100 (inclusive) displaying a custom error message.
let between1and100Custom =
Validators.Int.between 1 100 (sprintf "%s must be between 1 and 100") "fieldName"
between1and100Custom 12 // Result<int, ValidationErrors>
greaterThan
Applies to: int16, int, int64, decimal, float, DateTime, DateTimeOffset, TimeSpan
open Validus
// Define a validator which checks if an int is greater than
// 100 displaying the standard error message.
let greaterThan100 =
Validators.Default.Int.greaterThan 100 "fieldName"
greaterThan100 12 // Result<int, ValidationErrors>
// Define a validator which checks if an int is greater than
// 100 displaying a custom error message.
let greaterThan100Custom =
Validators.Int.greaterThan 100 (sprintf "%s must be greater than 100") "fieldName"
greaterThan100Custom 12 // Result<int, ValidationErrors>
lessThan
Applies to: int16, int, int64, decimal, float, DateTime, DateTimeOffset, TimeSpan
open Validus
// Define a validator which checks if an int is less than
// 100 displaying the standard error message.
let lessThan100 =
Validators.Default.Int.lessThan 100 "fieldName"
lessThan100 12 // Result<int, ValidationErrors>
// Define a validator which checks if an int is less than
// 100 displaying a custom error message.
let lessThan100Custom =
Validators.Int.lessThan 100 (sprintf "%s must be less than 100") "fieldName"
lessThan100Custom 12 // Result<int, ValidationErrors>
String specific validators
betweenLen
Applies to: string
open Validus
// Define a validator which checks if a string is between
// 1 and 100 chars displaying the standard error message.
let between1and100Chars =
Validators.Default.String.betweenLen 1 100 "fieldName"
between1and100Chars "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string is between
// 1 and 100 chars displaying a custom error message.
let between1and100CharsCustom =
Validators.String.betweenLen 1 100 (sprintf "%s must be between 1 and 100 chars") "fieldName"
between1and100CharsCustom "validus" // Result<string, ValidationErrors>
equalsLen
Applies to: string
open Validus
// Define a validator which checks if a string is equals to
// 100 chars displaying the standard error message.
let equals100Chars =
Validators.Default.String.equalsLen 100 "fieldName"
equals100Chars "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string is equals to
// 100 chars displaying a custom error message.
let equals100CharsCustom =
Validators.String.equalsLen 100 (sprintf "%s must be 100 chars") "fieldName"
equals100CharsCustom "validus" // Result<string, ValidationErrors>
greaterThanLen
Applies to: string
open Validus
// Define a validator which checks if a string is greater than
// 100 chars displaying the standard error message.
let greaterThan100Chars =
Validators.Default.String.greaterThanLen 100 "fieldName"
greaterThan100Chars "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string is greater than
// 100 chars displaying a custom error message.
let greaterThan100CharsCustom =
Validators.String.greaterThanLen 100 (sprintf "%s must be greater than 100 chars") "fieldName"
greaterThan100CharsCustom "validus" // Result<string, ValidationErrors>
lessThanLen
Applies to: string
open Validus
// Define a validator which checks if a string is less tha
// 100 chars displaying the standard error message.
let lessThan100Chars =
Validators.Default.String.lessThanLen 100 "fieldName"
lessThan100Chars "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string is less tha
// 100 chars displaying a custom error message.
let lessThan100CharsCustom =
Validators.String.lessThanLen 100 (sprintf "%s must be less than 100 chars") "fieldName"
lessThan100CharsCustom "validus" // Result<string, ValidationErrors>
empty
Applies to: string
open Validus
// Define a validator which checks if a string is empty
// displaying the standard error message.
let stringIsEmpty =
Validators.Default.String.empty "fieldName"
stringIsEmpty "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string is empty
// displaying a custom error message.
let stringIsEmptyCustom =
Validators.String.empty (sprintf "%s must be empty") "fieldName"
stringIsEmptyCustom "validus" // Result<string, ValidationErrors>
notEmpty
Applies to: string
open Validus
// Define a validator which checks if a string is not empty
// displaying the standard error message.
let stringIsNotEmpty =
Validators.Default.String.notEmpty "fieldName"
stringIsNotEmpty "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string is not empty
// displaying a custom error message.
let stringIsNotEmptyCustom =
Validators.String.notEmpty (sprintf "%s must not be empty") "fieldName"
stringIsNotEmptyCustom "validus" // Result<string, ValidationErrors>
pattern
(Regular Expressions)
Applies to: string
open Validus
// Define a validator which checks if a string matches the
// provided regex displaying the standard error message.
let stringIsChars =
Validators.Default.String.pattern "[a-z]" "fieldName"
stringIsChars "validus" // Result<string, ValidationErrors>
// Define a validator which checks if a string matches the
// provided regex displaying a custom error message.
let stringIsCharsCustom =
Validators.String.pattern "[a-z]" (sprintf "%s must follow the pattern [a-z]") "fieldName"
stringIsCharsCustom "validus" // Result<string, ValidationErrors>
Find a bug?
There's an issue for that.
License
Built with ♥ by Pim Brouwers in Toronto, ON. Licensed under Apache License 2.0.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- FSharp.Core (>= 4.5.2)
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 |
---|---|---|
4.1.4 | 3,233 | 3/15/2024 |
4.1.3 | 5,301 | 7/15/2023 |
4.1.2 | 8,376 | 1/12/2023 |
4.1.1 | 284 | 1/12/2023 |
4.1.0 | 529 | 12/26/2022 |
4.0.3 | 325 | 12/21/2022 |
4.0.2 | 745 | 11/22/2022 |
4.0.1 | 2,000 | 9/19/2022 |
4.0.0 | 556 | 9/5/2022 |
3.1.0 | 388 | 9/1/2022 |
3.0.1 | 2,295 | 12/9/2021 |
3.0.0 | 345 | 9/1/2021 |
2.0.3 | 383 | 8/17/2021 |
2.0.2 | 338 | 8/13/2021 |
2.0.1 | 342 | 8/6/2021 |
2.0.0 | 377 | 7/6/2021 |
1.4.2 | 329 | 6/28/2021 |
1.4.1 | 397 | 6/23/2021 |
1.3.1 | 369 | 6/15/2021 |
1.3.0 | 308 | 6/4/2021 |
1.2.0 | 317 | 6/2/2021 |
1.1.0 | 347 | 6/2/2021 |
1.0.1 | 382 | 4/22/2021 |
1.0.0 | 511 | 11/28/2020 |
1.0.0-alpha5 | 271 | 11/23/2020 |
1.0.0-alpha4 | 280 | 11/23/2020 |
1.0.0-alpha3 | 276 | 11/23/2020 |
1.0.0-alpha2 | 231 | 11/21/2020 |
1.0.0-alpha1 | 281 | 11/20/2020 |