Zoka
Zoka

Reputation: 2352

ASP.NET Core validation: switch off server validation for some properties dynamically

Is there a way to switch off validation of some model properties based on the bound model value. Example:

public class ContactModel
{
    public string Name { get; set; }
    public string ContactType { get; set; }
    [Required]
    public string Phone { get; set; }
    [Required]
    public string Email { get; set; }
}

This is some view model, which should retrieve the name of the person, and either his Phone or Email. If user select one of these, it should validate only the selected one. But when Phone is selected, and Email is empty, the ModelState.IsValid == false and vice versa.

I have tried to hook into the validation process (custom ValidationAttribute, IValidatableObject) but with no success, as the ValidationContext doesn't provide any useful context - I need to access the ContactType value when validating Phone/Email. I have even tried to create custom binder, but supressing validation in that stage doesn't provide intended results as it only sets the state of validation to Unvalidated, which is considered as ModelState.IsValid.

Only solution I have found so far is to create ActionFilter/PageFilter which will find if there is any model of ContactModel type, and remove the validation errors for unused fields from ModelState. But I don't feel comfortable with such solution (doesn't seems right to me). Have any ideas?

Upvotes: 1

Views: 320

Answers (1)

Brando Zhang
Brando Zhang

Reputation: 28032

According to your description, I suggest you could consider using custom CustomModelValidator and custom ModelValidator to achieve your requirement.

By using this, we could get the whole model by using its container value.

More details, you could refer to below codes:

CustomModelValidatorProvider class:

    public void CreateValidators(ModelValidatorProviderContext context)
    {
        if (context.ModelMetadata.ContainerType == typeof(ContactModel))
        {
            context.Results.Add(new ValidatorItem
            {
                Validator = new ContactModelValidator(),
                IsReusable = true
            });
        }
    }

    public class ContactModelValidator : IModelValidator
    {
        private static readonly object _emptyValidationContextInstance = new object();
        public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
        {
            var validationResults = new List<ModelValidationResult>();


            if (validationContext.ModelMetadata.Name == "Name" && validationContext.Model == null)
            {
                var validationResult = new ModelValidationResult("", "Name is required");

                validationResults.Add(validationResult);

            }
            if (validationContext.ModelMetadata.Name == "ContactType" && validationContext.Model == null)
            {
                var validationResult = new ModelValidationResult("", "ContactType is required");

                validationResults.Add(validationResult);
            }


            if (validationContext.ModelMetadata.Name == "ContactType" && validationContext.Model == null)
            {
                var validationResult = new ModelValidationResult("", "ContactType is required");

                validationResults.Add(validationResult);
            }

            if (validationContext.ModelMetadata.Name == "Phone" || validationContext.ModelMetadata.Name == "Email")
            {
                if (((ContactModel)validationContext.Container).Phone == null && ((ContactModel)validationContext.Container).Email == null)
                {
                    var validationResult = new ModelValidationResult("", "Phone or Email is required");

                    validationResults.Add(validationResult);
                }
            }

       



            return validationResults;
        }
    }

Then add it into startup.cs ConfigureServices method:

        services.AddControllers(options=> {
            options.ModelValidatorProviders.Add(new CustomModelValidatorProvider()); 
        });

Result:

enter image description here

Upvotes: 1

Related Questions