Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

780
Vistas
¿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 Respuestas
Responde la pregunta

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 Denunciar

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 Denunciar

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda