How to build a dynamic fluentvalidation dotnet core

ngovu.dl@gmail.com | 224 day | 425


At the moment, Web-API very populate so many members have a problem with "Model Validation" in them have me, this is the main reason I write the article so I hope my article will solve your problem like me.

First of all, you need to install a package as below:

dotnet add package FluentValidation --version 8.5.0

If you don't know FluentValidation please access the link to get more information about that.

Okay, now I will so you with a basic "FluentValidation" work, first i will create a model with 1 class validate for a model like this.

  public class AddOrEditCategoryDto
    {
        public string Id { get; set; }
        public string Name { get; set; }

    }

    public class AddOrEditCategoryDtoValidator : AbstractValidator<AddOrEditCategoryDto>
    {
        public AddOrEditCategoryDtoValidator()
        {
            RuleFor(x => x.Id).NotEmpty();

            RuleFor(x => x.Name).NotEmpty().MaximumLength(2);
        }
    }

And how to use it.

 [HttpPost]
        public IActionResult Create([FromBody]AddOrEditCategoryDto dto)
        {

            var validator = new AddOrEditCategoryDtoValidator();
            
            var result = validator.Validate(dto);

            if(!result.IsValid) return BadRequest(result.Errors);

            return Success();

        }

 So you see when I use "Fluentvalidation" the first step we need to create a validator instance the put the model in there, so the step loop many times in our system, and now I will show you reduce the step, and return an error suitable.

How to do that?

 

  1. As you see with block code loop many times
var validator = new AddOrEditCategoryDtoValidator();
            
var result = validator.Validate(dto);

So now we need to create 1 dynamic "init an instance" for this code as above like that.

  public static ValidationDto CheckValidation<TDto>(TDto model)
        {
            if (model == null) return new ValidationDto(false);

            string assemblyQualifiedName = $"{typeof(TDto).Namespace}.{typeof(TDto).Name}Validator, {typeof(TDto).Assembly}";

            Type type = Type.GetType(assemblyQualifiedName);

            IValidator validator = (IValidator)Activator.CreateInstance(type);

            return new ValidationDto(validator.Validate(model));
        }

With block code as above, I'm using Reflection , this is lib support we create an instance with type or string.

This steps the same with "var validator = new AddOrEditCategoryDtoValidator();", then I start check model and customize a response message in "ValidationDto" like that.

public class ValidationDto
    {
        public bool IsValid { get; private set; }

        public object Errors { get; private set; }

        public ValidationDto() : this(true)
        {
        }

        public ValidationDto(bool isValid)
        {
            IsValid = isValid;
        }

        public ValidationDto(ValidationResult validationResult)
        {
            IsValid = validationResult.IsValid;
            if (!validationResult.IsValid)
            {
                Errors = GetErrors(validationResult.Errors);
            }
        }

        private object GetErrors(IList<ValidationFailure> Errors)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append('{');

            int length = Errors.Count();

            foreach (var error in Errors.Select((item, i) => new
            {
                item = item,
                i = i
            }))
            {
                if (error.i > 0 && error.i < length) sb.Append(',');

                sb.AppendFormat("\"{0}\":\"{1}\"", FirstCharToLower(error.item.PropertyName), error.item.ErrorMessage.Replace("'", ""));

            }

            sb.Append('}');
            
            return JsonSerializer.Deserialize(sb.ToString(), typeof(object));
        }

        private static string FirstCharToLower(string input)
        {
            switch (input)
            {
                case null: throw new ArgumentNullException(nameof(input));
                case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
                default: return input.FirstOrDefault().ToString().ToLower() + input.Substring(1);
            }
        }
    }

As you see in this class we have 2 properties:

public bool IsValid { get; private set; }

public object Errors { get; private set; }
  1. Model "valid or not"
  2. The error message we will return an object ==> JSON.

Also in class, we have 3 contractors:

 public ValidationDto() : this(true)
        {
        }

        public ValidationDto(bool isValid)
        {
            IsValid = isValid;
        }

        public ValidationDto(ValidationResult validationResult)
        {
            IsValid = validationResult.IsValid;

            if (!IsValid)
            {
                Errors = GetErrors(validationResult.Errors);
            }
        }
  1. Create an empty contractor and it always returns one valid validation.
  2. The contractor has one parameter, you can set the validation of "model valid or not".
  3. The contractor has one parameter and pushes 1 validation instance "var validator = new AddOrEditCategoryDtoValidator();" and in this contractor, we will check model is "valid or not" if it's not we will compose a message to return to the client.
private object GetErrors(IList<ValidationFailure> Errors)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append('{');

            int length = Errors.Count();

            foreach (var error in Errors.Select((item, i) => new
            {
                item = item,
                i = i
            }))
            {
                if (error.i > 0 && error.i < length) sb.Append(',');

                sb.AppendFormat("\"{0}\":\"{1}\"", FirstCharToLower(error.item.PropertyName), error.item.ErrorMessage.Replace("'", ""));

            }

            sb.Append('}');
            
            return JsonSerializer.Deserialize(sb.ToString(), typeof(object));
        }

With block code as above, we will loop all the errors from "fluent validation" then customize a message expected to return.

So how to use it.

[HttpPost]
        public IActionResult Create([FromBody]AddOrEditCategoryDto dto)
        {
            var result = CheckValidation(dto);

            if (!result.IsValid) return BadRequest(result.Errors);

            ////TODO Your logic

            return Success();

        }

As you see with the code very clean, and easier now we will show you a JSON response return.

c-sharp.vn

As you see this time, the server will return a JSON error message it's popular with Font-End developer.

So I hope this article will help you if you don't understand or need to ask something do not hesitate to comments below. 

Source Code


Top Articles

Bất Đầu Với WebApi Và Dot Net Core (.Net Core)

1131 day
Butter Ngo
Views 6823
Comments 0

Repository Và Unit Of Work (Entity Framework)

1050 day
ndtung449@gmail.com
Views 4161
Comments 0

Dot Net Core Bearer Token With (JWT) (.Net Core)

1063 day
Butter Ngo
Views 4156
Comments 0

Bắt Đầu Với Dot NET Core (.Net Core)

1142 day
Butter Ngo
Views 3833
Comments 0

Top Question

Bi lỗi Invalid Column Name khi sử dụng LinQ (.Net)

990 day
Bảo Dương
Views 1058
Answers 2

Làm thế nào để lấy information từ token (.Net Core)

380 day
ngovu.dl@gmail.com
Views 604
Answers 1

.NET CORE API JWT (.Net Core)

283 day
huynhminhnhut97@gmail.com
Views 594
Answers 2