-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
Description
Describe the bug
When using ValidateDataAnnotations to validate a configuration hierarchy, you can only validate top level members of the class. Any properties that are types which also include validation are NOT validated.
Perhaps this was viewed as a design decision, but I think it'd be a lot more consistent if automatic data annotation validation worked in the same fashion as mvc model validation.
To Reproduce
- Design the classes which represent the nested configuration
public class AnnotatedOptions
{
[Required]
public string Required { get; set; }
[StringLength(5, ErrorMessage = "Too long.")]
public string StringLength { get; set; }
[Range(-5, 5, ErrorMessage = "Out of range.")]
public int IntRange { get; set; }
public AnnotatedOptionsSubsection AnnotatedOptionSubsection { get; set; }
}
public class AnnotatedOptionsSubsection
{
[Range(-5, 5, ErrorMessage = "Really out of range.")]
public int IntRange2 { get; set; }
}- Register the options
var services= new ServiceCollection();
services
.AddOptions<AnnotatedOptions>()
.Configure(o =>
{
o.StringLength = "111111";
o.IntRange = 10;
o.AnnotatedOptionSubsection = new AnnotatedOptionsSubsection
{
IntRange2 = 10000
};
})
.ValidateDataAnnotations();- Set up a controller which retrieves the options
[Route("api/[controller]")]
[ApiController]
public class ConfigController : ControllerBase
{
private readonly AnnotatedOptions _configuration;
public ConfigController(IOptionsMonitor<AnnotatedOptions> configuration)
{
try
{
_configuration = configuration.CurrentValue;
}
catch (OptionsValidationException ex)
{
throw new Exception(string.Join(@"\r\n", ex.Failures), ex);
}
}
// GET api/config/
[HttpGet("{configKey}")]
public ActionResult<AnnotatedOptions> Get()
{
return _configuration;
}
}- Invoke the controller method and observe the error results
System.Exception: DataAnnotation validation failed for members Required with the error 'The Required field is required.'.
DataAnnotation validation failed for members StringLength with the error 'Too long.'.
DataAnnotation validation failed for members IntRange with the error 'Out of range.'. ---> Microsoft.Extensions.Options.OptionsValidationException: Exception of type 'Microsoft.Extensions.Options.OptionsValidationException' was thrown.
at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
at Microsoft.Extensions.Options.OptionsMonitor`1.<>c__DisplayClass10_0.<Get>b__0()
at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy`1.CreateValue()
at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd(String name, Func`1 createOptions)
at Microsoft.Extensions.Options.OptionsMonitor`1.Get(String name)
at Microsoft.Extensions.Options.OptionsMonitor`1.get_CurrentValue()
at PF.ClinicalEndpoint.Host.Controllers.ConfigController..ctor(IOptionsMonitor`1 configuration) in C:\dev\test\ClinicalEndpoint\src\Host\Controllers\ConfigController.cs:line 19
--- End of inner exception stack trace ---
at PF.ClinicalEndpoint.Host.Controllers.ConfigController..ctor(IOptionsMonitor`1 configuration) in C:\dev\test\ClinicalEndpoint\src\Host\Controllers\ConfigController.cs:line 23
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
at PF.Core.WebApi.Middleware.ExceptionHandlingMiddleware.InvokeAsync(HttpContext httpContext)
at PF.Core.WebApi.Middleware.RequestLoggingMiddleware.InvokeAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
```
### Expected behavior
Another error in the list of failures for the `IntRange2` field which reads as follows
`DataAnnotation validation failed for members IntRange2 with the error 'Really out of range.'`
rcollina, vanbukin, Hardell, CameronAavik, DillonN and 31 moreLazaroOnline and teisnet