How would you validate emails in a minimal API project?

332 Views Asked by At

Using [EmailAddress] in a controller-based API is sufficient to also include email validation errors (as far as I know), however doing the same in a minimal API doesn't have that feature. A colleague suggested that this is the case because minimal APIs don't have support yet for IModelValidator.
How would you address this issue?

So far I've tried using jsteadfast/EmailValidation, but it seems to fail me. I attempted to use [Email] on top of a string I wanted to validate, however that doesn't seem to have the effect I wanted.

2

There are 2 best solutions below

1
On

I have no idea what the 'minimal API project' you mentioned means. Assuming you want to validate an email address without any dependency but only system libraries.

If you want to know if a string is an Email address, simply call:

using System.ComponentModel.DataAnnotations;

bool isValid = new EmailAddressAttribute().IsValid("[email protected]");

In case you want to validate a class which has Email address as a property, which is common in API projects:

You can try to add a static validator first:

using System.ComponentModel.DataAnnotations;

public static (bool isValid, string[] errors) Validate(object input)
{
    var context = new ValidationContext(input);
    var results = new List<ValidationResult>();

    if (Validator.TryValidateObject(input, context, results, true))
    {
        return (true, Array.Empty<string>());
    }
    else
    {
        return (false, results.Select(t => t.ErrorMessage).ToArray());
    }
}

Then build a model to be validated:

using System.ComponentModel.DataAnnotations;

public class Widget
{
    [EmailAddress]
    public string Email { get; set; }

    [Required(ErrorMessage = "The {0} is required!")]
    public int? Id { get; set; }

    [Required]
    [MinLength(10, ErrorMessage = "The {0} requires min length: {1}")]
    public string Name { get; set; }

    [Range(1, 100)]
    public decimal Price { get; set; }
}

Now you can validate it directly in Main() or any controller action code.

public static void Main(string[] args)
{
    var widget = new Widget
    {
        Email = "[email protected]",
        Price = 1557,
        Name = "test"
    };

    var (isValid, errors) = Validate(widget);

    Console.WriteLine($"Is valid: {isValid}");

    foreach (var error in errors)
    {
        Console.WriteLine(error);
    }
}

demo validate in cosnole app

0
On

A colleague suggested that this is the case because minimal APIs don't have support yet for IModelValidator. How would you address this issue?

Using this package O9d.AspNet.FluentValidation that's built on top of FluentValidation this can be achieved easily for minimal api's.

Firstly, you will need to add the following packages:

dotnet add package FluentValidation.AspNetCore --version 11.3.0

and

dotnet add package O9d.AspNet.FluentValidation --version 0.1.1

Add the validator to your Model like this:

public class CustomerRequest
{
    public int Id { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }

    public string? Email { get; set; }

    public class Validator : AbstractValidator<CustomerRequest>
    {
        public Validator()
        {
            RuleFor(x => x.Email).EmailAddress().WithMessage("Please provide valid email"); ;
        }
    }
}

Then in your Program.cs add this to register the validators:

builder.Services.AddValidatorsFromAssemblyContaining<Program>();

Last step then is to add the attribute [Validate] to the request and the method WithValidationFilter() which can either be added to the route group i.e:

var root = app.MapGroup("core").WithValidationFilter();

or the router handler itself:

root.MapPost("/weatherforecast", ([Validate] CustomerRequest customer) =>
{
    return customer;
}).WithValidationFilter();