forked from ServiceStack/ServiceStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAbstractValidator.cs
More file actions
226 lines (197 loc) · 9.75 KB
/
AbstractValidator.cs
File metadata and controls
226 lines (197 loc) · 9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#region License
// Copyright (c) Jeremy Skinner (http://www.jeremyskinner.co.uk)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// The latest version of this file can be found at http://www.codeplex.com/FluentValidation
#endregion
using ServiceStack.Common;
using ServiceStack.ServiceInterface;
namespace ServiceStack.FluentValidation
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Internal;
using Results;
using Validators;
/// <summary>
/// Base class for entity validator classes.
/// </summary>
/// <typeparam name="T">The type of the object being validated</typeparam>
public abstract class AbstractValidator<T> : IValidator<T>, IEnumerable<IValidationRule> {
readonly TrackingCollection<IValidationRule> nestedValidators = new TrackingCollection<IValidationRule>();
private static Func<CascadeMode> s_cascadeMode = () => ValidatorOptions.CascadeMode;
private Func<CascadeMode> cascadeMode = s_cascadeMode;
/// <summary>
/// Sets the cascade mode for all rules within this validator.
/// </summary>
public CascadeMode CascadeMode {
get { return cascadeMode(); }
set { cascadeMode = () => value; }
}
ValidationResult IValidator.Validate(object instance) {
instance.Guard("Cannot pass null to Validate.");
if(! ((IValidator)this).CanValidateInstancesOfType(instance.GetType())) {
throw new InvalidOperationException(string.Format("Cannot validate instances of type '{0}'. This validator can only validate instances of type '{1}'.", instance.GetType().Name, typeof(T).Name));
}
return Validate((T)instance);
}
ValidationResult IValidator.Validate(ValidationContext context) {
context.Guard("Cannot pass null to Validate");
var newContext = new ValidationContext<T>((T)context.InstanceToValidate, context.PropertyChain, context.Selector) {
IsChildContext = context.IsChildContext
};
return Validate(newContext);
}
/// <summary>
/// Validates the specified instance
/// </summary>
/// <param name="instance">The object to validate</param>
/// <returns>A ValidationResult object containing any validation failures</returns>
public virtual ValidationResult Validate(T instance) {
return Validate(new ValidationContext<T>(instance, new PropertyChain(), new DefaultValidatorSelector()));
}
/// <summary>
/// Validates the specified instance.
/// </summary>
/// <param name="context">Validation Context</param>
/// <returns>A ValidationResult object containing any validation failures.</returns>
public virtual ValidationResult Validate(ValidationContext<T> context) {
context.Guard("Cannot pass null to Validate");
var failures = nestedValidators.SelectMany(x => x.Validate(context)).ToList();
return new ValidationResult(failures);
}
/// <summary>
/// Adds a rule to the current validator.
/// </summary>
/// <param name="rule"></param>
public void AddRule(IValidationRule rule) {
nestedValidators.Add(rule);
}
/// <summary>
/// Creates a <see cref="IValidatorDescriptor" /> that can be used to obtain metadata about the current validator.
/// </summary>
public virtual IValidatorDescriptor CreateDescriptor() {
return new ValidatorDescriptor<T>(nestedValidators);
}
bool IValidator.CanValidateInstancesOfType(Type type) {
return typeof(T).IsAssignableFrom(type);
}
/// <summary>
/// Defines a validation rule for a specify property.
/// </summary>
/// <example>
/// RuleFor(x => x.Surname)...
/// </example>
/// <typeparam name="TProperty">The type of property being validated</typeparam>
/// <param name="expression">The expression representing the property to validate</param>
/// <returns>an IRuleBuilder instance on which validators can be defined</returns>
public IRuleBuilderInitial<T, TProperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression) {
expression.Guard("Cannot pass null to RuleFor");
var rule = PropertyRule.Create(expression, () => CascadeMode);
AddRule(rule);
var ruleBuilder = new RuleBuilder<T, TProperty>(rule);
return ruleBuilder;
}
/// <summary>
/// Defines a custom validation rule using a lambda expression.
/// If the validation rule fails, it should return a instance of a <see cref="ValidationFailure">ValidationFailure</see>
/// If the validation rule succeeds, it should return null.
/// </summary>
/// <param name="customValidator">A lambda that executes custom validation rules.</param>
public void Custom(Func<T, ValidationFailure> customValidator) {
customValidator.Guard("Cannot pass null to Custom");
AddRule(new DelegateValidator<T>(x => new[] { customValidator(x) }));
}
/// <summary>
/// Defines a custom validation rule using a lambda expression.
/// If the validation rule fails, it should return an instance of <see cref="ValidationFailure">ValidationFailure</see>
/// If the validation rule succeeds, it should return null.
/// </summary>
/// <param name="customValidator">A lambda that executes custom validation rules</param>
public void Custom(Func<T, ValidationContext<T>, ValidationFailure> customValidator) {
customValidator.Guard("Cannot pass null to Custom");
AddRule(new DelegateValidator<T>((x, ctx) => new[] { customValidator(x, ctx) }));
}
/// <summary>
/// Defines a RuleSet that can be used to group together several validators.
/// </summary>
/// <param name="ruleSetName">The name of the ruleset.</param>
/// <param name="action">Action that encapsulates the rules in the ruleset.</param>
public void RuleSet(string ruleSetName, Action action) {
ruleSetName.Guard("A name must be specified when calling RuleSet.");
action.Guard("A ruleset definition must be specified when calling RuleSet.");
using (nestedValidators.OnItemAdded(r => r.RuleSet = ruleSetName)) {
action();
}
}
/// <summary>
/// Defines a RuleSet that can be used to provide specific validation rules for specific HTTP methods (GET, POST...)
/// </summary>
/// <param name="appliesTo">The HTTP methods where this rule set should be used.</param>
/// <param name="action">Action that encapuslates the rules in the ruleset.</param>
public void RuleSet(ApplyTo appliesTo, Action action)
{
var httpMethods = appliesTo.ToString().Split(',')
.SafeConvertAll(x => x.Trim().ToUpper());
foreach (var httpMethod in httpMethods)
{
RuleSet(httpMethod, action);
}
}
/// <summary>
/// Defines a condition that applies to several rules
/// </summary>
/// <param name="predicate">The condition that should apply to multiple rules</param>
/// <param name="action">Action that encapsulates the rules.</param>
/// <returns></returns>
public void When(Func<T, bool> predicate, Action action) {
var propertyRules = new List<PropertyRule>();
Action<IValidationRule> onRuleAdded = rule => {
var propertyRule = rule as PropertyRule;
if(propertyRule != null) {
propertyRules.Add(propertyRule);
}
};
using(nestedValidators.OnItemAdded(onRuleAdded)) {
action();
}
// Must apply the predictae after the rule has been fully created to ensure any rules-specific conditions have already been applied.
propertyRules.ForEach(x => x.ApplyCondition(predicate.CoerceToNonGeneric()));
}
/// <summary>
/// Defiles an inverse condition that applies to several rules
/// </summary>
/// <param name="predicate">The condition that should be applied to multiple rules</param>
/// <param name="action">Action that encapsulates the rules</param>
public void Unless(Func<T, bool> predicate, Action action) {
When(x => !predicate(x), action);
}
/// <summary>
/// Returns an enumerator that iterates through the collection of validation rules.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
public IEnumerator<IValidationRule> GetEnumerator() {
return nestedValidators.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
}