Skip to content

Commit 59c6f2d

Browse files
committed
Tweaks after API review
1 parent 4d776c0 commit 59c6f2d

File tree

10 files changed

+66
-81
lines changed

10 files changed

+66
-81
lines changed

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,10 @@ 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
@@ -31,6 +19,18 @@ Microsoft.AspNetCore.Http.Validation.ValidatableTypeAttribute
3119
Microsoft.AspNetCore.Http.Validation.ValidatableTypeAttribute.ValidatableTypeAttribute() -> void
3220
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo
3321
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo.ValidatableTypeInfo(System.Type! type, System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo!>! members) -> void
22+
Microsoft.AspNetCore.Http.Validation.ValidateContext
23+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentDepth.get -> int
24+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentDepth.set -> void
25+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentValidationPath.get -> string!
26+
Microsoft.AspNetCore.Http.Validation.ValidateContext.CurrentValidationPath.set -> void
27+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidateContext() -> void
28+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.get -> System.ComponentModel.DataAnnotations.ValidationContext?
29+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationContext.set -> void
30+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationErrors.get -> System.Collections.Generic.Dictionary<string!, string![]!>?
31+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationErrors.set -> void
32+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationOptions.get -> Microsoft.AspNetCore.Http.Validation.ValidationOptions!
33+
Microsoft.AspNetCore.Http.Validation.ValidateContext.ValidationOptions.set -> void
3434
Microsoft.AspNetCore.Http.Validation.ValidationOptions
3535
Microsoft.AspNetCore.Http.Validation.ValidationOptions.MaxDepth.get -> int
3636
Microsoft.AspNetCore.Http.Validation.ValidationOptions.MaxDepth.set -> void
@@ -40,6 +40,6 @@ Microsoft.AspNetCore.Http.Validation.ValidationOptions.TryGetValidatableTypeInfo
4040
Microsoft.AspNetCore.Http.Validation.ValidationOptions.ValidationOptions() -> void
4141
Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions
4242
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
43+
virtual Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
44+
virtual Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
45+
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 & 19 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,
@@ -70,14 +70,9 @@ public ValidatablePropertyInfo(
7070
/// <returns>An array of validation attributes to apply to this member.</returns>
7171
protected abstract ValidationAttribute[] GetValidationAttributes();
7272

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>
73+
/// <inheritdoc />
7974
/// <returns>A task representing the asynchronous operation.</returns>
80-
public virtual async ValueTask ValidateAsync(object? value, ValidatableContext context, CancellationToken cancellationToken)
75+
public virtual async Task ValidateAsync(object? value, ValidateContext context, CancellationToken cancellationToken)
8176
{
8277
Debug.Assert(context.ValidationContext is not null);
8378

@@ -86,14 +81,14 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
8681
var validationAttributes = GetValidationAttributes();
8782

8883
// Calculate and save the current path
89-
var originalPrefix = context.Prefix;
84+
var originalPrefix = context.CurrentValidationPath;
9085
if (string.IsNullOrEmpty(originalPrefix))
9186
{
92-
context.Prefix = Name;
87+
context.CurrentValidationPath = Name;
9388
}
9489
else
9590
{
96-
context.Prefix = $"{originalPrefix}.{Name}";
91+
context.CurrentValidationPath = $"{originalPrefix}.{Name}";
9792
}
9893

9994
context.ValidationContext.DisplayName = DisplayName;
@@ -106,20 +101,20 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
106101

107102
if (result is not null && result != ValidationResult.Success)
108103
{
109-
context.AddValidationError(context.Prefix, [result!.ErrorMessage!]);
110-
context.Prefix = originalPrefix; // Restore prefix
104+
context.AddValidationError(context.CurrentValidationPath, [result!.ErrorMessage!]);
105+
context.CurrentValidationPath = originalPrefix; // Restore prefix
111106
return;
112107
}
113108
}
114109

115110
// Validate any other attributes
116-
ValidateValue(propertyValue, context.Prefix, validationAttributes);
111+
ValidateValue(propertyValue, context.CurrentValidationPath, validationAttributes);
117112

118113
// Check if we've reached the maximum depth before validating complex properties
119114
if (context.CurrentDepth >= context.ValidationOptions.MaxDepth)
120115
{
121116
throw new InvalidOperationException(
122-
$"Maximum validation depth of {context.ValidationOptions.MaxDepth} exceeded at '{context.Prefix}'. " +
117+
$"Maximum validation depth of {context.ValidationOptions.MaxDepth} exceeded at '{context.CurrentValidationPath}'. " +
123118
"This is likely caused by a circular reference in the object graph. " +
124119
"Consider increasing the MaxDepth in ValidationOptions if deeper validation is required.");
125120
}
@@ -133,11 +128,11 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
133128
if (PropertyType.IsEnumerable() && propertyValue is System.Collections.IEnumerable enumerable)
134129
{
135130
var index = 0;
136-
var currentPrefix = context.Prefix;
131+
var currentPrefix = context.CurrentValidationPath;
137132

138133
foreach (var item in enumerable)
139134
{
140-
context.Prefix = $"{currentPrefix}[{index}]";
135+
context.CurrentValidationPath = $"{currentPrefix}[{index}]";
141136

142137
if (item != null)
143138
{
@@ -152,7 +147,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
152147
}
153148

154149
// Restore prefix to the property name before validating the next item
155-
context.Prefix = currentPrefix;
150+
context.CurrentValidationPath = currentPrefix;
156151
}
157152
else if (propertyValue != null)
158153
{
@@ -168,7 +163,7 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
168163
{
169164
// Always decrement the depth counter and restore prefix
170165
context.CurrentDepth--;
171-
context.Prefix = originalPrefix;
166+
context.CurrentValidationPath = originalPrefix;
172167
}
173168

174169
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
{

src/Http/Http.Abstractions/src/Validation/ValidatableContext.cs renamed to src/Http/Http.Abstractions/src/Validation/ValidateContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Http.Validation;
88
/// <summary>
99
/// Represents the context for validating a validatable object.
1010
/// </summary>
11-
public sealed class ValidatableContext
11+
public sealed class ValidateContext
1212
{
1313
/// <summary>
1414
/// Gets or sets the validation context used for validating objects that implement <see cref="IValidatableObject"/> or have <see cref="ValidationAttribute"/>.
@@ -20,7 +20,7 @@ public sealed class ValidatableContext
2020
/// Gets or sets the prefix used to identify the current object being validated in a complex object graph.
2121
/// This is used to build property paths in validation error messages (e.g., "Customer.Address.Street").
2222
/// </summary>
23-
public string Prefix { get; set; } = string.Empty;
23+
public string CurrentValidationPath { get; set; } = string.Empty;
2424

2525
/// <summary>
2626
/// Gets or sets the validation options that control validation behavior,

0 commit comments

Comments
 (0)