Asp.net-Core

在 Asp.net Core 中使用 Swagger 進行流暢的驗證

  • June 30, 2021

我目前正在使用Fluent Validation而不是Data Annotations用於我的 Web api,並將 swagger 用於 API 文件。流暢的驗證規則沒有反映在 swagger 模型中,因為我無法使用 swagger 模式過濾器配置流暢的驗證規則。

這個部落格很好地解釋了將它與 ASP.net MVC 一起使用。但我無法將其配置為在 ASP.net Core 中使用它。

到目前為止,我已經嘗試了以下程式碼,但我無法獲得驗證器類型。

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
           }
       }
   }
}

經過搜尋,我終於發現我需要IValidationFactory驗證器實例。

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);
   }
}

並將此類添加到 swaggerGen 選項

options.SchemaFilter<AddFluentValidationRules>();

我根據 Mujahid Daud Khan 的回答創建了 github 項目和 nuget 包。我進行了重新設計以支持可擴展性並支持其他驗證器。

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

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

注意:對於 WebApi,請參閱:https ://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation.WebApi

支持的驗證器

  • INotNullValidator (NotNull)
  • INotEmptyValidator (NotEmpty)
  • ILengthValidator(長度、最小長度、最大長度、精確長度)
  • IRegularExpressionValidator(電子郵件,匹配)
  • IComparisonValidator(GreaterThan、GreaterThanOrEqual、LessThan、LessThanOrEqual)
  • IBetweenValidator(InclusiveBetween,ExclusiveBetween)

用法

1. 在你的 web 項目中引用包:

<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. 修改 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 範例模型和驗證器

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);
   }
}

隨意添加問題!

引用自:https://stackoverflow.com/questions/44638195