Added ParameterInfo argument to InvalidResultFactory
Maybe too niche, but only thing missing for me was ability to grab some custom attributes on the parameter being validated during the creation of the validation response.
This pull request simply added a ParameterInfo Parameter property to the ValidateableParameterDescriptor, and then inside CreateValidationFilter when calling InvalidResultFactory passed it in...
var validationResult = await validator.ValidateAsync(
new ValidationContext<object>(argument)
);
if (!validationResult.IsValid)
{
return options.InvalidResultFactory(validationResult, descriptor.Parameter);
}
Added one more commit. Want to write up a blog explaining a little more clearly, but here is a SO question explaining similar issue (https://stackoverflow.com/questions/75191596/how-to-handle-multipartbodylengthlimit-in-minimal-apis-with-custom-model-binding - see my own answer to see my final solution).
Essentially, if you have custom model binding (not sure if same thing happens in default binding if binding fails, assume the same), the BindAsync method runs before any endpoint filters. So you don't have the chance try/catch any exceptions. So I introduced an optional ProcessBindingValidations delegate on the ValidationFilterOptions that is called after the validator.ValidateAsync call.
There may be a better way to do this, but essentially in my custom BindAsync, I catch any bind exceptions and place a List<> in HttpContext.Items, then inside my ProcessBindingValidations action, I look for any binding exceptions and if found add them to the ValidationResult.Errors.
Here is my sample:
options.ProcessBindingValidations = ( invocationContext, parameterInfo, validationResult ) =>
{
var customInputParseErrors = invocationContext.HttpContext.Items[ $"BindErrors:{parameterInfo.ParameterType.GenericTypeArguments[ 0 ]}" ] as List<BindingParseError>;
if ( customInputParseErrors != null )
{
foreach ( var ciError in customInputParseErrors )
{
if ( !validationResult.Errors.Any( e => e.PropertyName == $"UserInputs.{ciError.Name}" ) )
{
var errorMessage = ciError.PropertyType switch
{
"DateTime" => localizer[ "{0}: {1} is not a valid date. Please enter a date in the format of {2}.", ciError.DisplayName, ciError.Value, System.Threading.Thread.CurrentThread.CurrentUICulture.DateTimeFormat.ShortDatePattern ],
"Int32" => localizer[ "{0}: {1} is not a valid numeric value. Please enter a number without decimal places.", ciError.DisplayName, ciError.Value ],
"Double" => localizer[ "{0}: {1} is not a valid numeric value. Please enter a number in the format of 0.##.", ciError.DisplayName, ciError.Value ],
_ => localizer[ "{0}: {1} is not a valid value.", ciError.DisplayName, ciError.Value ]
};
validationResult.Errors.Add( new ValidationFailure( ciError.Name, errorMessage, ciError.Value ) );
}
}
}
};
Thanks for raising the PR. I see the value in exposing the parameter descriptor to the invalid result factory but it doesn't seem right that we're adding a hook for something that is happening during model binding. I would be more inclined to have an exception filter of some kind to handle this case.
That said, if you can push a simplified example API to GitHub, demonstrating problem you're facing then I can have a look into whether it makes sense to handle it as part of the filter.