问题
I am following this example: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation and trying to implement my own custom attribute for validation.
Now, the viewmodel has two fields that I wish to access from inside this method, so that they can be rendered with the "data-val" attributes. My question is, how can I get say a property called "Myprop" from context here? When I debug I can se the information under context.ActionContext.ViewData.Model but I have no way of getting that info other then during the debug when I use Visual Studio "quick watch" feature. The custom attributes are on properties that are on the viewmodel.
public void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
var year = _year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
回答1:
I ran into a similar problem. The short answer is that you can't from there. You only have access to the metadata from there, not the actual model. The reason for this is that your model metadata and validation based on it are done on first time use, and then cached. So you'll never be able to change what validation rules to return based on the model via an attribute decorator.
If you need to dynamically decide which client side data-val-*
attributes to render based off the instance/content of your model, you would need to inherit from DefaultValidationHtmlAttributeProvider
, instead of using attributes, and override the AddValidationAttributes method. It's the only way I've found to do this so far. This is because inside of this method, you have access to the ModelExplorer
public class CustomValidationHtmlAttributeProvider : DefaultValidationHtmlAttributeProvider
{
private readonly IModelMetadataProvider metadataProvider;
public CustomValidationHtmlAttributeProvider(IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, ClientValidatorCache clientValidatorCache)
: base(optionsAccessor, metadataProvider, clientValidatorCache)
{
this.metadataProvider = metadataProvider;
}
public override void AddValidationAttributes(ViewContext viewContext, ModelExplorer modelExplorer, IDictionary<string, string> attributes)
{
//base implimentation
base.AddValidationAttributes(viewContext, modelExplorer, attributes);
//re-create the validation context (since it's encapsulated inside of the base implimentation)
var context = new ClientModelValidationContext(viewContext, modelExplorer.Metadata, metadataProvider, attributes);
//Only proceed if it's the model you need to do custom logic for
if (!(modelExplorer.Container.Model is MyViewModelClass model) || !modelExplorer.Metadata.PropertyName == "Myprop") return;
//Do stuff!
var validationAttributeAdapterProvider = viewContext.HttpContext.RequestServices.GetRequiredService<IValidationAttributeAdapterProvider>();
if (model.Myprop)
{
var validationAdapter = (RequiredAttributeAdapter)validationAttributeAdapterProvider.GetAttributeAdapter(new RequiredAttribute(), null);
validationAdapter.Attribute.ErrorMessage = "You not enter right stuff!";
validationAdapter.AddValidation(context);
}
}
}
And then register this class in the ConfigureServices()
of your Startup
public void ConfigureServices(IServiceCollection services)
{
//All your other DI stuff here
//register the new ValidationHtmlAttributeProvider
services.AddSingleton<ValidationHtmlAttributeProvider, PseudoAttributeValidationHtmlAttributeProvider>();
}
The downside, is that if you have multiple models you need to do this for, it gets really ugly really fast. If anyone has found a better method, I'd love to hear it :-)
来源:https://stackoverflow.com/questions/45939333/access-model-data-in-addvalidation-method-asp-net-core-custom-validation