Implementing FluentValidation with ASP.NET Core

FluentValidation for me is a validation framework that enables readability and more cleaner code into our application. There are a predefined set of rules that already predefined from the framework using Lambda Expression which makes the framework more readable and very neat.

For this post, we will create ASP.NET Core WebApi Project.

Configure The Project

Installing FluentValidation.AspNetCore Nuget using this command prompt in the Package Manager Console:

Install-Package FluentValidation.AspNetCore -Version 8.5.0

Creating Our First Validator

Let’s say we will create a Validator that will restrict Customers to had the same email address. For that purpose, I will create the first mock of DataContext and the Validator.

    public class Customer
    {
        public string Name { get; set; }
        public string Email { get; set; }
    }

    public interface IDataContext
    {
        List<Customer> Customers { get; set; }
    }

    public class DataContext : IDataContext
    {
        public List<Customer> Customers { get; set; }

        public DataContext()
        {
            Customers = new []
            {
                new Customer { Name = "Temmy Wahyu Raharjo", Email = "temmy@temmy.com"},
                new Customer { Name = "Inge Agustiningrum", Email = "inge@temmy.com"},
                new Customer { Name = "Feronic Oktarini", Email = "fefe@temmy.com"},
            }.ToList();
        }
    }

CustomerValidator:

  public class CustomerValidator : AbstractValidator<Customer>
    {
        public CustomerValidator(IDataContext dataContext)
        {
            RuleFor(e => e.Name).NotEmpty();
            RuleFor(e => e.Email).NotEmpty();
            RuleFor(e => e.Email)
                .Must(email => !dataContext.Customers.Any(c => c.Email == email))
                .WithMessage(customer => $"Customer using {customer.Email} already exists");
        }
    }

Wrapping It Up

We will wrap all the works with a Dependency Injector. For this sample, we will use ASP.NET Core Dependency Injector.

Startup.cs

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IDataContext, DataContext>();
            services.AddMvc()
                .AddFluentValidation().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddTransient<IValidator<Customer>, CustomerValidator>();

            services.Configure<ApiBehaviorOptions>(opts =>
            {
                opts.InvalidModelStateResponseFactory = (context) =>
                {
                    var errors = context.ModelState.Values.SelectMany(a => a.Errors.Select(e => e.ErrorMessage)).ToList();
                    var result = new
                    {
                        Code = "err",
                        Message = "Validation Errors",
                        Errors = errors
                    };

                    return new BadRequestObjectResult(result);
                };
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }

In line 13, we adding Dependency Injection for IDataContext. This means that if got a class that needs IDataContext, we will substitute it with DataContext. The other setting is we just want to create 1 single implementation of this class per App (Singleton Pattern).

In line 14, we let the services know that we will add FluentValidation.

In line 16, we register our validator. If got validation for the Customer, we will invoke CustomerValidator.

In Line 18 – 32, we configure if got validation errors, we will throw a new object with 3 attributes (Code, Message, and Errors).

Invoke

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.