Fluent Validation with Swagger in Asp.net Core

后端 未结 2 593
悲哀的现实
悲哀的现实 2020-12-28 19:07

I am currently using Fluent Validation instead of Data Annotations for my Web api and using swagger for API documentation. Fluent validation rules

相关标签:
2条回答
  • 2020-12-28 19:53

    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>();
    
    0 讨论(0)
  • 2020-12-28 19:55

    I've created github project and nuget package based on Mujahid Daud Khan answer. I made redesign to support extensibility and supported other validators.

    github: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation

    nuget: https://www.nuget.org/packages/MicroElements.Swashbuckle.FluentValidation

    Note: For WebApi see: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation.WebApi

    Supported validators

    • INotNullValidator (NotNull)
    • INotEmptyValidator (NotEmpty)
    • ILengthValidator (Length, MinimumLength, MaximumLength, ExactLength)
    • IRegularExpressionValidator (Email, Matches)
    • IComparisonValidator (GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual)
    • IBetweenValidator (InclusiveBetween, ExclusiveBetween)

    Usage

    1. Reference packages in your web project:

    <PackageReference Include="FluentValidation.AspNetCore" Version="7.5.2" />
    <PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="0.4.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="2.3.0" />
    

    2. Change Startup.cs

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddMvc()
            // Adds fluent validators to Asp.net
            .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<CustomerValidator>());
    
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
            // Adds fluent validation rules to swagger
            c.AddFluentValidationRules();
        });
    }
    
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app
            .UseMvc()
            // Adds swagger
            .UseSwagger();
    
        // Adds swagger UI
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        });
    }
    

    Swagger Sample model and validator

    public class Sample
    {
        public string PropertyWithNoRules { get; set; }
    
        public string NotNull { get; set; }
        public string NotEmpty { get; set; }
        public string EmailAddress { get; set; }
        public string RegexField { get; set; }
    
        public int ValueInRange { get; set; }
        public int ValueInRangeExclusive { get; set; }
    }
    
    public class SampleValidator : AbstractValidator<Sample>
    {
        public SampleValidator()
        {
            RuleFor(sample => sample.NotNull).NotNull();
            RuleFor(sample => sample.NotEmpty).NotEmpty();
            RuleFor(sample => sample.EmailAddress).EmailAddress();
            RuleFor(sample => sample.RegexField).Matches(@"(\d{4})-(\d{2})-(\d{2})");
    
            RuleFor(sample => sample.ValueInRange).GreaterThanOrEqualTo(5).LessThanOrEqualTo(10);
            RuleFor(sample => sample.ValueInRangeExclusive).GreaterThan(5).LessThan(10);
        }
    }
    

    Feel free to add issues!

    0 讨论(0)
提交回复
热议问题