From 3a132e4af5756d3b41c6b1afcbebc712f248863e Mon Sep 17 00:00:00 2001 From: Jeremy Skinner Date: Tue, 21 Nov 2017 19:49:43 +0000 Subject: [PATCH] Open up private methods in ValidationVisitor - Allows for other validation libraries to customize validation behaviour Addresses #7014 --- .../Validation/ValidationVisitor.cs | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Validation/ValidationVisitor.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Validation/ValidationVisitor.cs index 494a798537..64231e2a78 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Validation/ValidationVisitor.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Validation/ValidationVisitor.cs @@ -15,19 +15,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation /// public class ValidationVisitor { - private readonly IModelValidatorProvider _validatorProvider; - private readonly IModelMetadataProvider _metadataProvider; - private readonly ValidatorCache _validatorCache; - private readonly ActionContext _actionContext; - private readonly ModelStateDictionary _modelState; - private readonly ValidationStateDictionary _validationState; - private readonly ValidationStack _currentPath; + protected IModelValidatorProvider ValidatorProvider { get; } + protected IModelMetadataProvider MetadataProvider { get; } + protected ValidatorCache Cache { get; } + protected ActionContext Context { get; } + protected ModelStateDictionary ModelState { get; } + protected ValidationStateDictionary ValidationState { get; } + protected ValidationStack CurrentPath { get; } - private object _container; - private string _key; - private object _model; - private ModelMetadata _metadata; - private IValidationStrategy _strategy; + protected object Container { get; set; } + protected string Key { get; set; } + protected object Model { get; set; } + protected ModelMetadata Metadata { get; set; } + protected IValidationStrategy Strategy { get; set; } /// /// Indicates whether validation of a complex type should be performed if validation fails for any of its children. The default behavior is false. @@ -64,15 +64,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation throw new ArgumentNullException(nameof(validatorCache)); } - _actionContext = actionContext; - _validatorProvider = validatorProvider; - _validatorCache = validatorCache; + Context = actionContext; + ValidatorProvider = validatorProvider; + Cache = validatorCache; - _metadataProvider = metadataProvider; - _validationState = validationState; + MetadataProvider = metadataProvider; + ValidationState = validationState; - _modelState = actionContext.ModelState; - _currentPath = new ValidationStack(); + ModelState = actionContext.ModelState; + CurrentPath = new ValidationStack(); } /// @@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation /// The model prefix key. /// The model object. /// true if the object is valid, otherwise false. - public bool Validate(ModelMetadata metadata, string key, object model) + public virtual bool Validate(ModelMetadata metadata, string key, object model) { return Validate(metadata, key, model, alwaysValidateAtTopLevel: false); } @@ -95,11 +95,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation /// The model object. /// If true, applies validation rules even if the top-level value is null. /// true if the object is valid, otherwise false. - public bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel) + public virtual bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel) { if (model == null && key != null && !alwaysValidateAtTopLevel) { - var entry = _modelState[key]; + var entry = ModelState[key]; if (entry != null && entry.ValidationState != ModelValidationState.Valid) { entry.ValidationState = ModelValidationState.Valid; @@ -117,23 +117,23 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation /// true if the node is valid, otherwise false. protected virtual bool ValidateNode() { - var state = _modelState.GetValidationState(_key); + var state = ModelState.GetValidationState(Key); // Rationale: we might see the same model state key used for two different objects. // We want to run validation unless it's already known that this key is invalid. if (state != ModelValidationState.Invalid) { - var validators = _validatorCache.GetValidators(_metadata, _validatorProvider); + var validators = Cache.GetValidators(Metadata, ValidatorProvider); var count = validators.Count; if (count > 0) { var context = new ModelValidationContext( - _actionContext, - _metadata, - _metadataProvider, - _container, - _model); + Context, + Metadata, + MetadataProvider, + Container, + Model); var results = new List(); for (var i = 0; i < count; i++) @@ -145,21 +145,21 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation for (var i = 0; i < resultsCount; i++) { var result = results[i]; - var key = ModelNames.CreatePropertyModelName(_key, result.MemberName); + var key = ModelNames.CreatePropertyModelName(Key, result.MemberName); // If this is a top-level parameter/property, the key would be empty, // so use the name of the top-level property - if (string.IsNullOrEmpty(key) && _metadata.PropertyName != null) + if (string.IsNullOrEmpty(key) && Metadata.PropertyName != null) { - key = _metadata.PropertyName; + key = Metadata.PropertyName; } - _modelState.TryAddModelError(key, result.Message); + ModelState.TryAddModelError(key, result.Message); } } } - state = _modelState.GetFieldValidationState(_key); + state = ModelState.GetFieldValidationState(Key); if (state == ModelValidationState.Invalid) { return false; @@ -168,7 +168,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation { // If the field has an entry in ModelState, then record it as valid. Don't create // extra entries if they don't exist already. - var entry = _modelState[_key]; + var entry = ModelState[Key]; if (entry != null) { entry.ValidationState = ModelValidationState.Valid; @@ -178,11 +178,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation } } - private bool Visit(ModelMetadata metadata, string key, object model) + protected virtual bool Visit(ModelMetadata metadata, string key, object model) { RuntimeHelpers.EnsureSufficientExecutionStack(); - if (model != null && !_currentPath.Push(model)) + if (model != null && !CurrentPath.Push(model)) { // This is a cycle, bail. return true; @@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation metadata = entry?.Metadata ?? metadata; var strategy = entry?.Strategy; - if (_modelState.HasReachedMaxErrors) + if (ModelState.HasReachedMaxErrors) { SuppressValidation(key); return false; @@ -202,18 +202,18 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation { // Use the key on the entry, because we might not have entries in model state. SuppressValidation(entry.Key); - _currentPath.Pop(model); + CurrentPath.Pop(model); return true; } using (StateManager.Recurse(this, key ?? string.Empty, metadata, model, strategy)) { - if (_metadata.IsEnumerableType) + if (Metadata.IsEnumerableType) { return VisitComplexType(DefaultCollectionValidationStrategy.Instance); } - if (_metadata.IsComplexType) + if (Metadata.IsComplexType) { return VisitComplexType(DefaultComplexObjectValidationStrategy.Instance); } @@ -223,26 +223,26 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation } // Covers everything VisitSimpleType does not i.e. both enumerations and complex types. - private bool VisitComplexType(IValidationStrategy defaultStrategy) + protected virtual bool VisitComplexType(IValidationStrategy defaultStrategy) { var isValid = true; - if (_model != null && _metadata.ValidateChildren) + if (Model != null && Metadata.ValidateChildren) { - var strategy = _strategy ?? defaultStrategy; + var strategy = Strategy ?? defaultStrategy; isValid = VisitChildren(strategy); } - else if (_model != null) + else if (Model != null) { // Suppress validation for the entries matching this prefix. This will temporarily set // the current node to 'skipped' but we're going to visit it right away, so subsequent // code will set it to 'valid' or 'invalid' - SuppressValidation(_key); + SuppressValidation(Key); } // Double-checking HasReachedMaxErrors just in case this model has no properties. // If validation has failed for any children, only validate the parent if ValidateComplexTypesIfChildValidationFails is true. - if ((isValid || ValidateComplexTypesIfChildValidationFails) && !_modelState.HasReachedMaxErrors) + if ((isValid || ValidateComplexTypesIfChildValidationFails) && !ModelState.HasReachedMaxErrors) { isValid &= ValidateNode(); } @@ -250,22 +250,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation return isValid; } - private bool VisitSimpleType() + protected virtual bool VisitSimpleType() { - if (_modelState.HasReachedMaxErrors) + if (ModelState.HasReachedMaxErrors) { - SuppressValidation(_key); + SuppressValidation(Key); return false; } return ValidateNode(); } - private bool VisitChildren(IValidationStrategy strategy) + protected virtual bool VisitChildren(IValidationStrategy strategy) { var isValid = true; - var enumerator = strategy.GetChildren(_metadata, _key, _model); - var parentEntry = new ValidationEntry(_metadata, _key, _model); + var enumerator = strategy.GetChildren(Metadata, Key, Model); + var parentEntry = new ValidationEntry(Metadata, Key, Model); while (enumerator.MoveNext()) { @@ -284,7 +284,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation return isValid; } - private void SuppressValidation(string key) + protected virtual void SuppressValidation(string key) { if (key == null) { @@ -293,26 +293,26 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation return; } - var entries = _modelState.FindKeysWithPrefix(key); + var entries = ModelState.FindKeysWithPrefix(key); foreach (var entry in entries) { entry.Value.ValidationState = ModelValidationState.Skipped; } } - private ValidationStateEntry GetValidationEntry(object model) + protected virtual ValidationStateEntry GetValidationEntry(object model) { - if (model == null || _validationState == null) + if (model == null || ValidationState == null) { return null; } ValidationStateEntry entry; - _validationState.TryGetValue(model, out entry); + ValidationState.TryGetValue(model, out entry); return entry; } - private struct StateManager : IDisposable + protected struct StateManager : IDisposable { private readonly ValidationVisitor _visitor; private readonly object _container; @@ -331,11 +331,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation { var recursifier = new StateManager(visitor, model); - visitor._container = visitor._model; - visitor._key = key; - visitor._metadata = metadata; - visitor._model = model; - visitor._strategy = strategy; + visitor.Container = visitor.Model; + visitor.Key = key; + visitor.Metadata = metadata; + visitor.Model = model; + visitor.Strategy = strategy; return recursifier; } @@ -345,22 +345,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation _visitor = visitor; _newModel = newModel; - _container = _visitor._container; - _key = _visitor._key; - _metadata = _visitor._metadata; - _model = _visitor._model; - _strategy = _visitor._strategy; + _container = _visitor.Container; + _key = _visitor.Key; + _metadata = _visitor.Metadata; + _model = _visitor.Model; + _strategy = _visitor.Strategy; } public void Dispose() { - _visitor._container = _container; - _visitor._key = _key; - _visitor._metadata = _metadata; - _visitor._model = _model; - _visitor._strategy = _strategy; + _visitor.Container = _container; + _visitor.Key = _key; + _visitor.Metadata = _metadata; + _visitor.Model = _model; + _visitor.Strategy = _strategy; - _visitor._currentPath.Pop(_newModel); + _visitor.CurrentPath.Pop(_newModel); } } }