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