Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

778
Views
¿Cómo escribir reglas de validación fluidas dentro de IValidateOptions usando FluentValidation?

Para mi aplicación de servicio de trabajador .Net 5, quiero validar las opciones implementando la interfaz IValidateOptions pero no quiero escribir mis propios mensajes de error. Es por eso que quiero hacer uso del paquete FluentValidation.AspNetCore .

Dado el modelo

 namespace App.Models { public class MyOptions { public string Text { get; set; } } }

Agregué reglas de validación.

 namespace App.ModelValidators { public class MyOptionsValidator : AbstractValidator<MyOptions> { public MyOptionsValidator() { RuleFor(model => model.Text).NotEmpty(); } } }

A continuación, estoy usando este validador dentro de la validación.

 namespace App.OptionsValidators { public class MyOptionsValidator : IValidateOptions<MyOptions> { private readonly IValidator<MyOptions> _validator; public MyOptionsValidator(IValidator<MyOptions> validator) { _validator = validator; } public ValidateOptionsResult Validate(string name, MyOptions options) { var validationResult = _validator.Validate(options); if (validationResult.IsValid) { return ValidateOptionsResult.Success; } return ValidateOptionsResult.Fail(validationResult.Errors.Select(validationFailure => validationFailure.ErrorMessage)); } } }

Por último, configuré el contenedor DI

 services.AddTransient<IValidator<MyOptions>, ModelValidators.MyOptionsValidator>(); services.AddSingleton<IValidateOptions<MyOptions>, OptionsValidators.MyOptionsValidator>(); services.Configure<MyOptions>(configuration.GetSection("My"));

Quisiera saber si esto se puede simplificar.

¿Tal vez pueda implementar la interfaz IValidateOptions , evitar el AbstractValidator y escribir reglas fluidas dentro del método .Validate() ?

Ejemplo de código de lo que quiero lograr

 namespace App.OptionsValidators { public class MyOptionsValidator : IValidateOptions<MyOptions> { public ValidateOptionsResult Validate(string name, MyOptions options) { var validationResult = options.Text.Should.Not.Be.Empty(); if (validationResult.IsValid) { return ValidateOptionsResult.Success; } return ValidateOptionsResult.Fail(validationResult.ErrorMessage); } } }

así que ya no necesito AbstractValidator<MyOptions> .


Mi primer acercamiento no estoy seguro

En lugar de usar FluentValidation, estoy usando DataAnnotations.

  • Agregué el atributo [Required] a la propiedad Text
  • Eliminé por completo la clase MyOptionsValidator : AbstractValidator<MyOptions>
  • Solo registro esto en la configuración DI

.

 services.AddSingleton<IValidateOptions<MyOptions>, OptionsValidators.MyOptionsValidator>(); services.Configure<MyOptions>(configuration.GetSection("My"));

Dentro MyOptionsValidator valido las opciones así

 public ValidateOptionsResult Validate(string name, MyOptions options) { var validationResults = new List<ValidationResult>(); if (!Validator.TryValidateObject(options, new ValidationContext(options), validationResults, true)) { return ValidateOptionsResult.Fail(validationResults.Select(validationResult => validationResult.ErrorMessage)); } return ValidateOptionsResult.Success; }

pero tal vez todavía hay mejores maneras :)

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Puedo simplificar usando System.ComponentModel.DataAnnotations

 public class MyOptions { [Required] public string Text { get; set; } }

luego puede personalizar su BadRequest a través de la canalización

 services.AddControllers() .ConfigureApiBehaviorOptions(opts => { opts.InvalidModelStateResponseFactory = context => { var problemDetails = new ViolationProblemDetails() { Instance = context.HttpContext.Request.Path, Status = StatusCodes.Status400BadRequest, Detail = "Please refer to the errors property for additional details." }; problemDetails.Violations = new List<Violation>(); foreach (var modelState in context.ModelState) { problemDetails.Violations.Add(new Violation() { Field = modelState.Key, Message = string.Join(",",modelState.Value.Errors.Select(a => a.ErrorMessage)) }); } return new BadRequestObjectResult(problemDetails); }; })

más información https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-5.0

over 4 years ago · Santiago Trujillo Report

0

Encontré el siguiente código en este artículo https://dejanstojanovic.net/aspnet/2020/april/validate-configurations-with-fluentvalidation-in-aspnet-core/

 public void ConfigureServices(IServiceCollection services) { services.Configure<EndpointsConfiguration>(Configuration.GetSection(nameof(EndpointsConfiguration))); services.AddSingleton<AbstractValidator<EndpointsConfiguration>, EndpointsConfigurationValidator>(); services.AddSingleton<EndpointsConfiguration>(container => { var config = container.GetService<IOptions<EndpointsConfiguration>>().Value; var validator = container.GetService<AbstractValidator<EndpointsConfiguration>>(); validator.Validate(config); return config; }); services.AddControllers(); }

De esta manera, puede tener toda su validación en la implementación de AbstractValidator y será más simple que su implementación actual. Al usar esto, no es necesario implementar IOptionsValidator , supongo. El artículo tiene una buena explicación, así que consulte para obtener más información.

over 4 years ago · Santiago Trujillo Report

0

Tengo una fuerte preferencia por utilizar el mismo enfoque para la validación en toda la pila y, en mi caso, esto es a través de FluentValidation. El siguiente sería mi enfoque.

Cree un nuevo validador base para sus validadores de opciones/configuraciones:

 public abstract class AbstractOptionsValidator<T> : AbstractValidator<T>, IValidateOptions<T> where T : class { public virtual ValidateOptionsResult Validate(string name, T options) { var validateResult = this.Validate(options); return validateResult.IsValid ? ValidateOptionsResult.Success : ValidateOptionsResult.Fail(validateResult.Errors.Select(x => x.ErrorMessage)); } }

Esto amplía FluentValidation AbstractValidator<T> para admitir IValidateOptions<T> . Ahora tiene una base que puede usar para todos sus validadores de opciones/configuraciones. Para los siguientes ajustes:

 public class FooSettings { public string Bar { get; set; } }

terminas con un validador típico:

 public class FooSettingsValidator : AbstractOptionsValidator<FooSettings>, IFooSettingsValidator { public FooSettingsValidator() { RuleFor(x => x.Bar).NotEmpty(); } }

Hágale saber al contenedor DI:

 serviceCollection.AddSingleton<IValidateOptions<FooSettings>, FooSettingsValidator>();

Si no hay nada incorporado para hacer lo anterior, miraría a Scrutor para convertir esto en un proceso automático.

Todo lo anterior me brinda todos los beneficios de usar FluentValidation, mientras utilizo el soporte de validación de opciones de primera clase que Microsoft nos ha brindado.

Ejemplo de trabajo de LINQPad:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

 using static FluentAssertions.FluentActions; void Main() { var fixture = new Fixture(); var validator = new FooSettingsValidator(); validator.Validate(fixture.Build<FooSettings>().Without(x => x.Bar).Create()).Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo(new string[] { "'Bar' must not be empty." }); validator.Validate(fixture.Build<FooSettings>().Create()).Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo(new string[] { }); using (var scope = ServiceProvider.Create(bar: null).CreateScope()) { Invoking(() => scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<FooSettings>>().Value).Should().Throw<OptionsValidationException>(); } using (var scope = ServiceProvider.Create(bar: "asdf").CreateScope()) { scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<FooSettings>>().Value.Bar.Should().Be("asdf"); } } // You can define other methods, fields, classes and namespaces here public class FooSettings { public string Bar { get; set; } } public interface IFooSettingsValidator : IValidator { } public class FooSettingsValidator : AbstractOptionsValidator<FooSettings>, IFooSettingsValidator { public FooSettingsValidator() { RuleFor(x => x.Bar).NotEmpty(); } } public abstract class AbstractOptionsValidator<T> : AbstractValidator<T>, IValidateOptions<T> where T : class { public virtual ValidateOptionsResult Validate(string name, T options) { var validateResult = this.Validate(options); return validateResult.IsValid ? ValidateOptionsResult.Success : ValidateOptionsResult.Fail(validateResult.Errors.Select(x => x.ErrorMessage)); } } public class ServiceProvider { public static IServiceProvider Create(string bar) { var serviceCollection = new ServiceCollection(); var config = new ConfigurationBuilder() .AddInMemoryCollection( new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("Foo:Bar", bar) }) .Build(); serviceCollection.AddSingleton<IConfiguration>(config); serviceCollection.AddOptions(); //serviceCollection.Configure<FooSettings>(config.GetSection("Foo")); serviceCollection.AddOptions<FooSettings>() .Bind(config.GetSection("Foo")); serviceCollection.AddSingleton<IValidateOptions<FooSettings>, FooSettingsValidator>(); serviceCollection.AddSingleton<IFooSettingsValidator, FooSettingsValidator>(); return serviceCollection.BuildServiceProvider(); } }
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!