1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. 2 3 using System; 4 using System.Collections.Generic; 5 using System.ComponentModel; 6 using System.Globalization; 7 using System.Web.Http.Controllers; 8 using System.Web.Http.Metadata; 9 using System.Web.Http.ModelBinding; 10 using System.Web.Http.Validation; 11 using System.Web.Http.ValueProviders; 12 13 namespace Microsoft.Web.Http.Data 14 { 15 internal static class DataControllerValidation 16 { ValidateObject(object o, List<ValidationResultInfo> validationErrors, HttpActionContext actionContext)17 internal static bool ValidateObject(object o, List<ValidationResultInfo> validationErrors, HttpActionContext actionContext) 18 { 19 // create a model validation node for the object 20 ModelMetadataProvider metadataProvider = actionContext.GetMetadataProvider(); 21 string modelStateKey = String.Empty; 22 ModelValidationNode validationNode = CreateModelValidationNode(o, metadataProvider, actionContext.ModelState, modelStateKey); 23 validationNode.ValidateAllProperties = true; 24 25 // add the node to model state 26 ModelState modelState = new ModelState(); 27 modelState.Value = new ValueProviderResult(o, String.Empty, CultureInfo.CurrentCulture); 28 actionContext.ModelState.Add(modelStateKey, modelState); 29 30 // invoke validation 31 validationNode.Validate(actionContext); 32 33 if (!actionContext.ModelState.IsValid) 34 { 35 foreach (var modelStateItem in actionContext.ModelState) 36 { 37 foreach (ModelError modelError in modelStateItem.Value.Errors) 38 { 39 validationErrors.Add(new ValidationResultInfo(modelError.ErrorMessage, new string[] { modelStateItem.Key })); 40 } 41 } 42 } 43 44 return actionContext.ModelState.IsValid; 45 } 46 CreateModelValidationNode(object o, ModelMetadataProvider metadataProvider, ModelStateDictionary modelStateDictionary, string modelStateKey)47 private static ModelValidationNode CreateModelValidationNode(object o, ModelMetadataProvider metadataProvider, ModelStateDictionary modelStateDictionary, string modelStateKey) 48 { 49 ModelMetadata metadata = metadataProvider.GetMetadataForType(() => 50 { 51 return o; 52 }, o.GetType()); 53 ModelValidationNode validationNode = new ModelValidationNode(metadata, modelStateKey); 54 55 // for this root node, recursively add all child nodes 56 HashSet<object> visited = new HashSet<object>(); 57 CreateModelValidationNodeRecursive(o, validationNode, metadataProvider, metadata, modelStateDictionary, modelStateKey, visited); 58 59 return validationNode; 60 } 61 CreateModelValidationNodeRecursive(object o, ModelValidationNode parentNode, ModelMetadataProvider metadataProvider, ModelMetadata metadata, ModelStateDictionary modelStateDictionary, string modelStateKey, HashSet<object> visited)62 private static void CreateModelValidationNodeRecursive(object o, ModelValidationNode parentNode, ModelMetadataProvider metadataProvider, ModelMetadata metadata, ModelStateDictionary modelStateDictionary, string modelStateKey, HashSet<object> visited) 63 { 64 if (visited.Contains(o)) 65 { 66 return; 67 } 68 visited.Add(o); 69 70 foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(o)) 71 { 72 // append the current property name to the model state path 73 string propertyKey = modelStateKey; 74 if (propertyKey.Length > 0) 75 { 76 propertyKey += "."; 77 } 78 propertyKey += property.Name; 79 80 // create the node for this property and add to the parent node 81 object propertyValue = property.GetValue(o); 82 metadata = metadataProvider.GetMetadataForProperty(() => 83 { 84 return propertyValue; 85 }, o.GetType(), property.Name); 86 ModelValidationNode childNode = new ModelValidationNode(metadata, propertyKey); 87 parentNode.ChildNodes.Add(childNode); 88 89 // add the property node to model state 90 ModelState modelState = new ModelState(); 91 modelState.Value = new ValueProviderResult(propertyValue, null, CultureInfo.CurrentCulture); 92 modelStateDictionary.Add(propertyKey, modelState); 93 94 if (propertyValue != null) 95 { 96 CreateModelValidationNodeRecursive(propertyValue, childNode, metadataProvider, metadata, modelStateDictionary, propertyKey, visited); 97 } 98 } 99 } 100 } 101 } 102