Skip to content

Commit 7ee3e1a

Browse files
committed
Tweaks after API review
1 parent 4d776c0 commit 7ee3e1a

File tree

10 files changed

+66
-97
lines changed

10 files changed

+66
-97
lines changed

src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,30 @@ Microsoft.AspNetCore.Http.ProducesResponseTypeMetadata.Description.get -> string
66
Microsoft.AspNetCore.Http.ProducesResponseTypeMetadata.Description.set -> void
77
Microsoft.AspNetCore.Http.Metadata.IProducesResponseTypeMetadata.Description.get -> string?
88
Microsoft.AspNetCore.Http.Validation.IValidatableInfo
9-
Microsoft.AspNetCore.Http.Validation.IValidatableInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidatableContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
9+
Microsoft.AspNetCore.Http.Validation.IValidatableInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
1010
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver
1111
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver.TryGetValidatableParameterInfo(System.Reflection.ParameterInfo! parameterInfo, out Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo) -> bool
1212
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver.TryGetValidatableTypeInfo(System.Type! type, out Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo) -> bool
13-
Microsoft.AspNetCore.Http.Validation.ValidatableContext
14-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.CurrentDepth.get -> int
15-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.CurrentDepth.set -> void
16-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.Prefix.get -> string!
17-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.Prefix.set -> void
18-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidatableContext() -> void
19-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidationContext.get -> System.ComponentModel.DataAnnotations.ValidationContext?
20-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidationContext.set -> void
21-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidationErrors.get -> System.Collections.Generic.Dictionary<string!, string![]!>?
22-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidationErrors.set -> void
23-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidationOptions.get -> Microsoft.AspNetCore.Http.Validation.ValidationOptions!
24-
Microsoft.AspNetCore.Http.Validation.ValidatableContext.ValidationOptions.set -> void
2513
Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo
2614
Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo.ValidatableParameterInfo(System.Type! parameterType, string! name, string! displayName) -> void
2715
Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo
28-
Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo.IsRequired.get -> bool
2916
Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo.ValidatablePropertyInfo(System.Type! declaringType, System.Type! propertyType, string! name, string! displayName) -> void
3017
Microsoft.AspNetCore.Http.Validation.ValidatableTypeAttribute
3118
Microsoft.AspNetCore.Http.Validation.ValidatableTypeAttribute.ValidatableTypeAttribute() -> void
3219
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo
3320
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo.ValidatableTypeInfo(System.Type! type, System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo!>! members) -> void
21+
Microsoft.AspNetCore.Http.Validation.ValidateContext
22+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentDepth.get -> int
23+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentDepth.set -> void
24+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentValidationPath.get -> string!
25+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentValidationPath.set -> void
26+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidateContext() -> void
27+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.get -> System.ComponentModel.DataAnnotations.ValidationContext?
28+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.set -> void
29+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationErrors.get -> System.Collections.Generic.Dictionary<string!, string![]!>?
30+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationErrors.set -> void
31+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationOptions.get -> Microsoft.AspNetCore.Http.Validation.ValidationOptions!
32+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationOptions.set -> void
3433
Microsoft.AspNetCore.Http.Validation.ValidationOptions
3534
Microsoft.AspNetCore.Http.Validation.ValidationOptions.MaxDepth.get -> int
3635
Microsoft.AspNetCore.Http.Validation.ValidationOptions.MaxDepth.set -> void
@@ -40,6 +39,6 @@ Microsoft.AspNetCore.Http.Validation.ValidationOptions.TryGetValidatableTypeInfo
4039
Microsoft.AspNetCore.Http.Validation.ValidationOptions.ValidationOptions() -> void
4140
Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions
4241
static Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<Microsoft.AspNetCore.Http.Validation.ValidationOptions!>? configureOptions = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
43-
virtual Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidatableContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
44-
virtual Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidatableContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
45-
virtual Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidatableContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
42+
virtual Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
43+
virtual Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
44+
virtual Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!

src/Http/Http.Abstractions/src/Validation/IValidatableInfo.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public interface IValidatableInfo
1313
/// </summary>
1414
/// <param name="value">The value to validate.</param>
1515
/// <param name="context">The validation context.</param>
16-
/// <param name="cancellationToken"></param>
17-
ValueTask ValidateAsync(object? value, ValidatableContext context, CancellationToken cancellationToken);
16+
/// <param name="cancellationToken">A cancellation token to support cancellation of the validation.</param>
17+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
18+
Task ValidateAsync(object? value, ValidateContext context, CancellationToken cancellationToken);
1819
}

src/Http/Http.Abstractions/src/Validation/ValidatableParameterInfo.cs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public abstract class ValidatableParameterInfo : IValidatableInfo
2020
/// <param name="parameterType">The <see cref="Type"/> associated with the parameter.</param>
2121
/// <param name="name">The parameter name.</param>
2222
/// <param name="displayName">The display name for the parameter.</param>
23-
public ValidatableParameterInfo(
23+
protected ValidatableParameterInfo(
2424
Type parameterType,
2525
string name,
2626
string displayName)
@@ -51,18 +51,12 @@ public ValidatableParameterInfo(
5151
/// <returns>An array of validation attributes to apply to this parameter.</returns>
5252
protected abstract ValidationAttribute[] GetValidationAttributes();
5353

54-
/// <summary>
55-
/// Validates the parameter value.
56-
/// </summary>
57-
/// <param name="value">The value to validate.</param>
58-
/// <param name="context">The context for the validation.</param>
59-
/// <param name="cancellationToken"></param>
60-
/// <returns>A task representing the asynchronous operation.</returns>
54+
/// <inheritdoc />
6155
/// <remarks>
6256
/// If the parameter is a collection, each item in the collection will be validated.
6357
/// If the parameter is not a collection but has a validatable type, the single value will be validated.
6458
/// </remarks>
65-
public virtual async ValueTask ValidateAsync(object? value, ValidatableContext context, CancellationToken cancellationToken)
59+
public virtual async Task ValidateAsync(object? value, ValidateContext context, CancellationToken cancellationToken)
6660
{
6761
Debug.Assert(context.ValidationContext is not null);
6862

@@ -83,7 +77,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
8377

8478
if (result is not null && result != ValidationResult.Success)
8579
{
86-
var key = string.IsNullOrEmpty(context.Prefix) ? Name : $"{context.Prefix}.{Name}";
80+
var key = string.IsNullOrEmpty(context.CurrentValidationPath) ? Name : $"{context.CurrentValidationPath}.{Name}";
8781
context.AddValidationError(key, [result.ErrorMessage!]);
8882
return;
8983
}
@@ -98,13 +92,13 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
9892
var result = attribute.GetValidationResult(value, context.ValidationContext);
9993
if (result is not null && result != ValidationResult.Success)
10094
{
101-
var key = string.IsNullOrEmpty(context.Prefix) ? Name : $"{context.Prefix}.{Name}";
95+
var key = string.IsNullOrEmpty(context.CurrentValidationPath) ? Name : $"{context.CurrentValidationPath}.{Name}";
10296
context.AddOrExtendValidationErrors(key, [result!.ErrorMessage!]);
10397
}
10498
}
10599
catch (Exception ex)
106100
{
107-
var key = string.IsNullOrEmpty(context.Prefix) ? Name : $"{context.Prefix}.{Name}";
101+
var key = string.IsNullOrEmpty(context.CurrentValidationPath) ? Name : $"{context.CurrentValidationPath}.{Name}";
108102
context.AddValidationError(key, [ex.Message]);
109103
}
110104
}
@@ -117,9 +111,9 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
117111
{
118112
if (item != null)
119113
{
120-
var itemPrefix = string.IsNullOrEmpty(context.Prefix)
114+
var itemPrefix = string.IsNullOrEmpty(context.CurrentValidationPath)
121115
? $"{Name}[{index}]"
122-
: $"{context.Prefix}.{Name}[{index}]";
116+
: $"{context.CurrentValidationPath}.{Name}[{index}]";
123117

124118
if (context.ValidationOptions.TryGetValidatableTypeInfo(item.GetType(), out var validatableType))
125119
{

src/Http/Http.Abstractions/src/Validation/ValidatablePropertyInfo.cs

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public abstract class ValidatablePropertyInfo : IValidatableInfo
1515
/// <summary>
1616
/// Creates a new instance of <see cref="ValidatablePropertyInfo"/>.
1717
/// </summary>
18-
public ValidatablePropertyInfo(
18+
protected ValidatablePropertyInfo(
1919
[param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
2020
Type declaringType,
2121
Type propertyType,
@@ -49,35 +49,15 @@ public ValidatablePropertyInfo(
4949
/// </summary>
5050
internal string DisplayName { get; }
5151

52-
/// <summary>
53-
/// Gets whether the member is enumerable.
54-
/// </summary>
55-
internal bool IsEnumerable { get; }
56-
57-
/// <summary>
58-
/// Gets whether the member is nullable.
59-
/// </summary>
60-
internal bool IsNullable { get; }
61-
62-
/// <summary>
63-
/// Gets whether the member is annotated with the <see cref="RequiredAttribute"/>.
64-
/// </summary>
65-
public bool IsRequired { get; }
66-
6752
/// <summary>
6853
/// Gets the validation attributes for this member.
6954
/// </summary>
7055
/// <returns>An array of validation attributes to apply to this member.</returns>
7156
protected abstract ValidationAttribute[] GetValidationAttributes();
7257

73-
/// <summary>
74-
/// Validates the member's value.
75-
/// </summary>
76-
/// <param name="value">The object containing the member to validate.</param>
77-
/// <param name="context">The context for the validation.</param>
78-
/// <param name="cancellationToken"></param>
58+
/// <inheritdoc />
7959
/// <returns>A task representing the asynchronous operation.</returns>
80-
public virtual async ValueTask ValidateAsync(object? value, ValidatableContext context, CancellationToken cancellationToken)
60+
public virtual async Task ValidateAsync(object? value, ValidateContext context, CancellationToken cancellationToken)
8161
{
8262
Debug.Assert(context.ValidationContext is not null);
8363

@@ -86,14 +66,14 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
8666
var validationAttributes = GetValidationAttributes();
8767

8868
// Calculate and save the current path
89-
var originalPrefix = context.Prefix;
69+
var originalPrefix = context.CurrentValidationPath;
9070
if (string.IsNullOrEmpty(originalPrefix))
9171
{
92-
context.Prefix = Name;
72+
context.CurrentValidationPath = Name;
9373
}
9474
else
9575
{
96-
context.Prefix = $"{originalPrefix}.{Name}";
76+
context.CurrentValidationPath = $"{originalPrefix}.{Name}";
9777
}
9878

9979
context.ValidationContext.DisplayName = DisplayName;
@@ -106,20 +86,20 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
10686

10787
if (result is not null && result != ValidationResult.Success)
10888
{
109-
context.AddValidationError(context.Prefix, [result!.ErrorMessage!]);
110-
context.Prefix = originalPrefix; // Restore prefix
89+
context.AddValidationError(context.CurrentValidationPath, [result!.ErrorMessage!]);
90+
context.CurrentValidationPath = originalPrefix; // Restore prefix
11191
return;
11292
}
11393
}
11494

11595
// Validate any other attributes
116-
ValidateValue(propertyValue, context.Prefix, validationAttributes);
96+
ValidateValue(propertyValue, context.CurrentValidationPath, validationAttributes);
11797

11898
// Check if we've reached the maximum depth before validating complex properties
11999
if (context.CurrentDepth >= context.ValidationOptions.MaxDepth)
120100
{
121101
throw new InvalidOperationException(
122-
$"Maximum validation depth of {context.ValidationOptions.MaxDepth} exceeded at '{context.Prefix}'. " +
102+
$"Maximum validation depth of {context.ValidationOptions.MaxDepth} exceeded at '{context.CurrentValidationPath}'. " +
123103
"This is likely caused by a circular reference in the object graph. " +
124104
"Consider increasing the MaxDepth in ValidationOptions if deeper validation is required.");
125105
}
@@ -133,11 +113,11 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
133113
if (PropertyType.IsEnumerable() && propertyValue is System.Collections.IEnumerable enumerable)
134114
{
135115
var index = 0;
136-
var currentPrefix = context.Prefix;
116+
var currentPrefix = context.CurrentValidationPath;
137117

138118
foreach (var item in enumerable)
139119
{
140-
context.Prefix = $"{currentPrefix}[{index}]";
120+
context.CurrentValidationPath = $"{currentPrefix}[{index}]";
141121

142122
if (item != null)
143123
{
@@ -152,7 +132,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
152132
}
153133

154134
// Restore prefix to the property name before validating the next item
155-
context.Prefix = currentPrefix;
135+
context.CurrentValidationPath = currentPrefix;
156136
}
157137
else if (propertyValue != null)
158138
{
@@ -168,7 +148,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
168148
{
169149
// Always decrement the depth counter and restore prefix
170150
context.CurrentDepth--;
171-
context.Prefix = originalPrefix;
151+
context.CurrentValidationPath = originalPrefix;
172152
}
173153

174154
void ValidateValue(object? val, string errorPrefix, ValidationAttribute[] validationAttributes)

src/Http/Http.Abstractions/src/Validation/ValidatableTypeInfo.cs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public abstract class ValidatableTypeInfo : IValidatableInfo
2121
/// </summary>
2222
/// <param name="type">The type being validated.</param>
2323
/// <param name="members">The members that can be validated.</param>
24-
public ValidatableTypeInfo(
24+
protected ValidatableTypeInfo(
2525
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type,
2626
IReadOnlyList<ValidatablePropertyInfo> members)
2727
{
@@ -41,13 +41,8 @@ public ValidatableTypeInfo(
4141
/// </summary>
4242
internal IReadOnlyList<ValidatablePropertyInfo> Members { get; }
4343

44-
/// <summary>
45-
/// Validates the specified value.
46-
/// </summary>
47-
/// <param name="value">The value to validate.</param>
48-
/// <param name="context">The validation context.</param>
49-
/// <param name="cancellationToken"></param>
50-
public virtual async ValueTask ValidateAsync(object? value, ValidatableContext context, CancellationToken cancellationToken)
44+
/// <inheritdoc />
45+
public virtual async Task ValidateAsync(object? value, ValidateContext context, CancellationToken cancellationToken)
5146
{
5247
Debug.Assert(context.ValidationContext is not null);
5348
if (value == null)
@@ -59,21 +54,21 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
5954
if (context.CurrentDepth >= context.ValidationOptions.MaxDepth)
6055
{
6156
throw new InvalidOperationException(
62-
$"Maximum validation depth of {context.ValidationOptions.MaxDepth} exceeded at '{context.Prefix}'. " +
57+
$"Maximum validation depth of {context.ValidationOptions.MaxDepth} exceeded at '{context.CurrentValidationPath}'. " +
6358
"This is likely caused by a circular reference in the object graph. " +
6459
"Consider increasing the MaxDepth in ValidationOptions if deeper validation is required.");
6560
}
6661

6762
try
6863
{
6964
var actualType = value.GetType();
70-
var originalPrefix = context.Prefix;
65+
var originalPrefix = context.CurrentValidationPath;
7166

7267
// First validate members
7368
for (var i = 0; i < _membersCount; i++)
7469
{
7570
await Members[i].ValidateAsync(value, context, cancellationToken);
76-
context.Prefix = originalPrefix;
71+
context.CurrentValidationPath = originalPrefix;
7772
}
7873

7974
// Then validate sub-types if any
@@ -86,7 +81,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
8681
if (context.ValidationOptions.TryGetValidatableTypeInfo(subType, out var subTypeInfo))
8782
{
8883
await subTypeInfo.ValidateAsync(value, context, cancellationToken);
89-
context.Prefix = originalPrefix;
84+
context.CurrentValidationPath = originalPrefix;
9085
}
9186
}
9287
}
@@ -123,7 +118,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
123118
}
124119

125120
// Always restore original prefix
126-
context.Prefix = originalPrefix;
121+
context.CurrentValidationPath = originalPrefix;
127122
}
128123
finally
129124
{

0 commit comments

Comments
 (0)