Custom Validator not working when binding on multiple complex objects
See original GitHub issueSystem Details
- FluentValidation version: 8.1.3
- Web Framework version: ASP.NET Core 2.2 (Razor Pages)
Issue Description
I am using two separate objects in ASP.Net Core 2.2 Razor Pages to bind data on.
Object A:
public string Title { get; set; }
public string Message { get; set; }
Object B:
public IFormFile FileToUpload { get; set; }
Each one has separate validators. Object A uses built in validators for NotNull()
. Object B uses a custom validator for which I am passing in a file validator service for my custom validation.
public class OptionalFileValidator : AbstractValidator<ObjectB>
{
public OptionalFileValidator(IFileValidate fileValidate)
{
RuleFor(file => file.FileToUpload).SetValidator(new CustomFileValidator(fileValidate));
}
}
The form that the user submits is very straightforward:
<form enctype="multipart/form-data">
<div class="form-group">
<input asp-for="ObjectA.Title" class="form-control" />
<span asp-validation-for="ObjectA.Title" class="text-danger"></span>
</div>
<div class="form-group">
<input asp-for="ObjectB.FileToUpload" type="file" accept=".png,.jpg,.bmp" /><br />
<span asp-validation-for="ObjectB.FileToUpload" class="text-danger"></span>
</div>
</form>
I tested two different scenarios:
- Have a separate object for the IFormFile to bind on (i.e. Object B)
- Include the IFormFile property into Object A
When the IFormFile exists in a separate object (Object B), model validation is not working correctly. The custom validator correctly identifies that the property failed model validation BUT its attaching the error to a modelstate key named “FileToUpload” instead of correctly appending it to “ObjectB.FileToUpload”.
If the IFormFile exists in the same object (Object A), this issue does not occur and custom validator is correctly appending the error to the modelstate key named “ObjectA.FileToUpload”.
Note that if I disable FluentValidation for ObjectB and manually run the validation in my controller and attach the error message to ObjectB.FileToUpload, everything works correctly. FluentValidation for some reason is not attaching the model error to the right instance.
My custom validator for reference:
public CustomFileValidator(IFileValidate fileValidate): base("{ErrorMessage}")
{
_fileValidate = fileValidate;
}
protected override async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation)
{
var fileToValidate = context.PropertyValue as IFormFile;
var (Valid, ErrorMessage) = await _fileValidate.ValidateFileAsync(fileToValidate);
if (!Valid)
{
context.MessageFormatter.AppendArgument("ErrorMessage", ErrorMessage);
return false;
}
return true;
}
In my custom validator, the PropertyValidatorContext is correctly identifying the instance as being “ObjectB.FileToUpload” yet its not attaching the error to that property. Any reason it works this way?
Issue Analytics
- State:
- Created 5 years ago
- Comments:5 (3 by maintainers)
Top GitHub Comments
I’ve opened an issue on the asp.net core repo reporting this. https://github.com/aspnet/AspNetCore/issues/7238
Very strange. If you leave the form field blank and try and submit the form, the error message is added correctly to ModelState with the key
ObjB.FileToValidate
. The problem only occurs if you actually supply a file to upload. The same thing happens if you removeObjA
completely, so it’s not related to having multiple models on the page.However, this doesn’t appear to be an issue with FluentValidation - ASP.NET itself is failing to generate the “ObjB” prefix in this case - it’s never being passed to FluentValidation. I’d say this is a bug in ASP.NET Core. It only happens for models that contain a reference to
IFormFile
, and only when a file is uploaded.For now you can work around it by explicitly adding
Name
to your BindProperty:Quite why this is happening I’m uncertain…debugging the model binding process is pretty difficult and I can’t see anything obvious.