Search code examples
asp.net-coreswaggerfluentvalidation

Fluent Validation with Swagger in Asp.net Core


I am currently using Fluent Validation instead of Data Annotations for my Web api and using swagger for API documentation. Fluent validation rules are not reflected in swagger model as i am unable to configure fluent validation rules with swagger schema filter.

This Blog has a good explanation for using it with ASP.net MVC. but i am unable to configure it to use it in ASP.net Core.

So far i have tried the following code but i am unable to get validator type.

services.AddSwaggerGen(options => options.SchemaFilter<AddFluentValidationRules>());

public class AddFluentValidationRules : ISchemaFilter
{
    public void Apply(Schema model, SchemaFilterContext context)
    {
        model.Required = new List<string>();
        var validator = GetValidator(type); // How?
        var validatorDescriptor = validator.CreateDescriptor();

        foreach (var key in model.Properties.Keys)
        {
            foreach (var propertyValidator in validatorDescriptor.GetValidatorsForMember(key))
            {
                 // Add to model properties as in blog
            }
        }
    }
}

Solution

  • After searching i have finally figured out that i needed to IValidationFactory for validator instance.

    public class AddFluentValidationRules : ISchemaFilter
    {
        private readonly IValidatorFactory _factory;
    
        /// <summary>
        ///     Default constructor with DI
        /// </summary>
        /// <param name="factory"></param>
        public AddFluentValidationRules(IValidatorFactory factory)
        {
            _factory = factory;
        }
    
        /// <summary>
        /// </summary>
        /// <param name="model"></param>
        /// <param name="context"></param>
        public void Apply(Schema model, SchemaFilterContext context)
        {
    
            // use IoC or FluentValidatorFactory to get AbstractValidator<T> instance
            var validator = _factory.GetValidator(context.SystemType);
            if (validator == null) return;
            if (model.Required == null)
                model.Required = new List<string>();
    
            var validatorDescriptor = validator.CreateDescriptor();
            foreach (var key in model.Properties.Keys)
            {
                foreach (var propertyValidator in validatorDescriptor
                    .GetValidatorsForMember(ToPascalCase(key)))
                {
                    if (propertyValidator is NotNullValidator 
                      || propertyValidator is NotEmptyValidator)
                        model.Required.Add(key);
    
                    if (propertyValidator is LengthValidator lengthValidator)
                    {
                        if (lengthValidator.Max > 0)
                            model.Properties[key].MaxLength = lengthValidator.Max;
    
                        model.Properties[key].MinLength = lengthValidator.Min;
                    }
    
                    if (propertyValidator is RegularExpressionValidator expressionValidator)
                        model.Properties[key].Pattern = expressionValidator.Expression;
    
                    // Add more validation properties here;
                }
            }
        }
    
        /// <summary>
        ///     To convert case as swagger may be using lower camel case
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        private static string ToPascalCase(string inputString)
        {
            // If there are 0 or 1 characters, just return the string.
            if (inputString == null) return null;
            if (inputString.Length < 2) return inputString.ToUpper();
            return inputString.Substring(0, 1).ToUpper() + inputString.Substring(1);
        }
    }
    

    and add this class to swaggerGen options

    options.SchemaFilter<AddFluentValidationRules>();