/
PropertyValidator.cs
161 lines (133 loc) · 6.41 KB
/
PropertyValidator.cs
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
#region License
// Copyright (c) .NET Foundation and contributors.
//
// 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 https://github.com/FluentValidation/FluentValidation
#endregion
namespace ServiceStack.FluentValidation.Validators {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Internal;
using Resources;
using Results;
public abstract class PropertyValidator : PropertyValidatorOptions, IPropertyValidator {
/// <inheritdoc />
//TODO: For FV 10 make this an explicit implementation.
public PropertyValidatorOptions Options => this;
[Obsolete("This constructor is deprecated and will be removed in FluentValidation 10. Override the GetDefaultMessageTemplate method instead.")]
protected PropertyValidator(IStringSource errorMessageSource) {
if(errorMessageSource == null) errorMessageSource = new StaticStringSource("No default error message has been specified.");
else if (errorMessageSource is LanguageStringSource l && l.ErrorCodeFunc == null)
l.ErrorCodeFunc = ctx => ErrorCodeSource?.GetString(ctx);
ErrorMessageSource = errorMessageSource;
}
[Obsolete("This constructor is deprecated and will be removed in FluentValidation 10. Override the GetDefaultMessageTemplate method instead.")]
protected PropertyValidator(string errorMessage) {
SetErrorMessage(errorMessage);
}
protected PropertyValidator() {
}
/// <summary>
/// Retrieves a localized string from the LanguageManager.
/// If an ErrorCode is defined for this validator, the error code is used as the key.
/// If no ErrorCode is defined (or the language manager doesn't have a translation for the error code)
/// then the fallback key is used instead.
/// </summary>
/// <param name="fallbackKey">The fallback key to use for translation, if no ErrorCode is available.</param>
/// <returns>The translated error message template.</returns>
protected string Localized(string fallbackKey) {
var errorCode = ErrorCode;
if (errorCode != null) {
string result = ValidatorOptions.Global.LanguageManager.GetString(errorCode);
if (!string.IsNullOrEmpty(result)) {
return result;
}
}
return ValidatorOptions.Global.LanguageManager.GetString(fallbackKey);
}
/// <inheritdoc />
public virtual IEnumerable<ValidationFailure> Validate(PropertyValidatorContext context) {
if (IsValid(context)) return Enumerable.Empty<ValidationFailure>();
PrepareMessageFormatterForValidationError(context);
return new[] { CreateValidationError(context) };
}
/// <inheritdoc />
public virtual async Task<IEnumerable<ValidationFailure>> ValidateAsync(PropertyValidatorContext context, CancellationToken cancellation) {
if (await IsValidAsync(context, cancellation)) return Enumerable.Empty<ValidationFailure>();
PrepareMessageFormatterForValidationError(context);
return new[] {CreateValidationError(context)};
}
/// <inheritdoc />
public virtual bool ShouldValidateAsynchronously(IValidationContext context) {
// If the user has applied an async condition, then always go through the async path
// even if validator is being run synchronously.
if (HasAsyncCondition) return true;
return false;
}
protected abstract bool IsValid(PropertyValidatorContext context);
#pragma warning disable 1998
protected virtual async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation) {
return IsValid(context);
}
#pragma warning restore 1998
/// <summary>
/// Prepares the <see cref="MessageFormatter"/> of <paramref name="context"/> for an upcoming <see cref="ValidationFailure"/>.
/// </summary>
/// <param name="context">The validator context</param>
protected virtual void PrepareMessageFormatterForValidationError(PropertyValidatorContext context) {
context.MessageFormatter.AppendPropertyName(context.DisplayName);
context.MessageFormatter.AppendPropertyValue(context.PropertyValue);
// If there's a collection index cached in the root context data then add it
// to the message formatter. This happens when a child validator is executed
// as part of a call to RuleForEach. Usually parameters are not flowed through to
// child validators, but we make an exception for collection indices.
if (context.ParentContext.RootContextData.TryGetValue("__FV_CollectionIndex", out var index)) {
// If our property validator has explicitly added a placeholder for the collection index
// don't overwrite it with the cached version.
if (!context.MessageFormatter.PlaceholderValues.ContainsKey("CollectionIndex")) {
context.MessageFormatter.AppendArgument("CollectionIndex", index);
}
}
}
/// <summary>
/// Creates an error validation result for this validator.
/// </summary>
/// <param name="context">The validator context</param>
/// <returns>Returns an error validation result.</returns>
protected virtual ValidationFailure CreateValidationError(PropertyValidatorContext context) {
var messageBuilderContext = new MessageBuilderContext(context, this);
var error = context.Rule.MessageBuilder != null
? context.Rule.MessageBuilder(messageBuilderContext)
: messageBuilderContext.GetDefaultMessage();
var failure = new ValidationFailure(context.PropertyName, error, context.PropertyValue);
#pragma warning disable 618
failure.FormattedMessageArguments = context.MessageFormatter.AdditionalArguments;
#pragma warning restore 618
failure.FormattedMessagePlaceholderValues = context.MessageFormatter.PlaceholderValues;
#pragma warning disable 618
failure.ErrorCode = ErrorCodeSource?.GetString(context) ?? ValidatorOptions.Global.ErrorCodeResolver(this);
#pragma warning restore 618
if (CustomStateProvider != null) {
failure.CustomState = CustomStateProvider(context);
}
if (SeverityProvider != null) {
failure.Severity = SeverityProvider(context);
}
return failure;
}
}
}