I have two worlds of validation mechanisms in my ASP.NET Core 3.1 application:
In
services.AddControllersWithViewsI add a customIValidationMetadataProvidertooptions.ModelMetadataDetailsProviders. It changes theErrorMessageof the defaultValidationAttributes that are defined in my model classes to something that can be localised. This happens for all models that are validated through MVC model-binding. The results are in the usual place ofModelState.Here's some code for that:
// In Startup.ConfigureServices: services.AddControllersWithViews(options => { options.ModelMetadataDetailsProviders.Add(new LocalizedValidationMetadataProvider()); }); internal class LocalizedValidationMetadataProvider : IValidationMetadataProvider { public void CreateValidationMetadata(ValidationMetadataProviderContext context) { foreach (object attribute in context.ValidationMetadata.ValidatorMetadata) { if (attribute is ValidationAttribute valAttr) { if (valAttr is RequiredAttribute reqAttr) reqAttr.ErrorMessage = "@input_required"; if (valAttr is MaxLengthAttribute maxLenAttr) maxLenAttr.ErrorMessage = $"@max_length {maxLenAttr.Length}"; } } } }When reading and validating data from other sources (like importing from a JSON file), I use the
Validatorobject to validate the models using their standardValidationAttributes and other mechanisms (IValidatableObject). The results are in a local list ofValidationResults.Here's what I've done so far:
var vc = new ValidationContext(importedModel); var results = new List<ValidationResult>(); bool isValid = Validator.TryValidateObject(importedModel, vc, results, true); if (!isValid) { // Here I need results with the localisable messages like "@input_required" // Instead, I get the default "The (Name) field is required." }
Now the problem is that in the second case, the default messages for the ValidationAttributes are generated and in the ValidationResults I have no reference to the source of these messages. I need to integrate the first case somehow into the second scenario.
The IValidationMetadataProvider implementation works on validation metadata found in a ValidationMetadataProviderContext instance. I tried to trace down where that comes from and how MVC model-binding uses it but I am a bit lost in the aspnetcore source code with all its interfaces.
How would I bring the ValidationContext of Validator together with the ValidationMetadataProviderContext of MVC?
Edit: Having read the relevant source code a bit further, I have the impression that it's not possible to use Validator like this. Alternatively, how could I use MVC's ModelState-related mechanisms for objects that did not come from a web form request but another arbitrary source? Can I tell one of MVC's classes to process an object as if it came from the request and through model binding?