Skip to content

Commit 4d776c0

Browse files
committed
Tweak more APIs
1 parent 669443e commit 4d776c0

20 files changed

+354
-312
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ Microsoft.AspNetCore.Http.Metadata.IProducesResponseTypeMetadata.Description.get
88
Microsoft.AspNetCore.Http.Validation.IValidatableInfo
99
Microsoft.AspNetCore.Http.Validation.IValidatableInfo.ValidateAsync(object? value, Microsoft.AspNetCore.Http.Validation.ValidatableContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
1010
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver
11-
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver.GetValidatableParameterInfo(System.Reflection.ParameterInfo! parameterInfo) -> Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo?
12-
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver.GetValidatableTypeInfo(System.Type! type) -> Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo?
11+
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver.TryGetValidatableParameterInfo(System.Reflection.ParameterInfo! parameterInfo, out Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo) -> bool
12+
Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver.TryGetValidatableTypeInfo(System.Type! type, out Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo) -> bool
1313
Microsoft.AspNetCore.Http.Validation.ValidatableContext
1414
Microsoft.AspNetCore.Http.Validation.ValidatableContext.CurrentDepth.get -> int
1515
Microsoft.AspNetCore.Http.Validation.ValidatableContext.CurrentDepth.set -> void
@@ -30,13 +30,13 @@ Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo.ValidatableProperty
3030
Microsoft.AspNetCore.Http.Validation.ValidatableTypeAttribute
3131
Microsoft.AspNetCore.Http.Validation.ValidatableTypeAttribute.ValidatableTypeAttribute() -> void
3232
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo
33-
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo.ValidatableTypeInfo(System.Type! type, System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo!>! members, bool implementsIValidatableObject, System.Collections.Generic.IReadOnlyList<System.Type!>? validatableSubTypes = null) -> void
33+
Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo.ValidatableTypeInfo(System.Type! type, System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo!>! members) -> 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
3737
Microsoft.AspNetCore.Http.Validation.ValidationOptions.Resolvers.get -> System.Collections.Generic.IList<Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver!>!
38-
Microsoft.AspNetCore.Http.Validation.ValidationOptions.TryGetValidatableParameterInfo(System.Reflection.ParameterInfo! parameterInfo, out Microsoft.AspNetCore.Http.Validation.ValidatableParameterInfo? validatableParameterInfo) -> bool
39-
Microsoft.AspNetCore.Http.Validation.ValidationOptions.TryGetValidatableTypeInfo(System.Type! type, out Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo? validatableTypeInfo) -> bool
38+
Microsoft.AspNetCore.Http.Validation.ValidationOptions.TryGetValidatableParameterInfo(System.Reflection.ParameterInfo! parameterInfo, out Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo) -> bool
39+
Microsoft.AspNetCore.Http.Validation.ValidationOptions.TryGetValidatableTypeInfo(System.Type! type, out Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableTypeInfo) -> bool
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!
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Reflection;
56

67
namespace Microsoft.AspNetCore.Http.Validation;
@@ -12,16 +13,20 @@ namespace Microsoft.AspNetCore.Http.Validation;
1213
public interface IValidatableInfoResolver
1314
{
1415
/// <summary>
15-
/// Gets validation type information for the specified type.
16+
/// Gets validation information for the specified type.
1617
/// </summary>
1718
/// <param name="type">The type to get validation information for.</param>
18-
/// <returns>The validation type information, or null if the type is not validatable.</returns>
19-
ValidatableTypeInfo? GetValidatableTypeInfo(Type type);
19+
/// <param name="validatableInfo">
20+
/// The output parameter that will contain the validatable information if found.
21+
/// </param>
22+
/// <returns><see langword="true" /> if the validatable type information was found; otherwise, false.</returns>
23+
bool TryGetValidatableTypeInfo(Type type, [NotNullWhen(true)] out IValidatableInfo? validatableInfo);
2024

2125
/// <summary>
22-
/// Gets validation parameter information for the specified parameter.
26+
/// Gets validation information for the specified parameter.
2327
/// </summary>
24-
/// <param name="parameterInfo">The parameter information to get validation for.</param>
25-
/// <returns>The validation parameter information, or null if the parameter is not validatable.</returns>
26-
ValidatableParameterInfo? GetValidatableParameterInfo(ParameterInfo parameterInfo);
28+
/// <param name="parameterInfo">The parameter to get validation information for.</param>
29+
/// <param name="validatableInfo">The output parameter that will contain the validatable information if found.</param>
30+
/// <returns><see langword="true" /> if the validatable parameter information was found; otherwise, false.</returns>
31+
bool TryGetValidatableParameterInfo(ParameterInfo parameterInfo, [NotNullWhen(true)] out IValidatableInfo? validatableInfo);
2732
}

src/Http/Http.Abstractions/src/Validation/RuntimeValidatableParameterInfoResolver.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,33 @@
33

44
using System.ComponentModel.DataAnnotations;
55
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Linq;
78
using System.Reflection;
89

910
namespace Microsoft.AspNetCore.Http.Validation;
1011

1112
internal class RuntimeValidatableParameterInfoResolver : IValidatableInfoResolver
1213
{
13-
public ValidatableParameterInfo? GetValidatableParameterInfo(ParameterInfo parameterInfo)
14+
public bool TryGetValidatableTypeInfo(Type type, [NotNullWhen(true)] out IValidatableInfo? validatableInfo)
15+
{
16+
validatableInfo = null;
17+
return false;
18+
}
19+
20+
public bool TryGetValidatableParameterInfo(ParameterInfo parameterInfo, [NotNullWhen(true)] out IValidatableInfo? validatableInfo)
1421
{
1522
Debug.Assert(parameterInfo.Name != null, "Parameter must have name");
1623
var validationAttributes = parameterInfo
1724
.GetCustomAttributes<ValidationAttribute>()
1825
.ToArray();
19-
return new RuntimeValidatableParameterInfo(
26+
validatableInfo = new RuntimeValidatableParameterInfo(
2027
parameterType: parameterInfo.ParameterType,
2128
name: parameterInfo.Name,
2229
displayName: GetDisplayName(parameterInfo),
2330
validationAttributes: validationAttributes
2431
);
32+
return true;
2533
}
2634

2735
private static string GetDisplayName(ParameterInfo parameterInfo)
@@ -35,11 +43,6 @@ private static string GetDisplayName(ParameterInfo parameterInfo)
3543
return parameterInfo.Name!;
3644
}
3745

38-
public ValidatableTypeInfo? GetValidatableTypeInfo(Type type)
39-
{
40-
return null;
41-
}
42-
4346
private class RuntimeValidatableParameterInfo(
4447
Type parameterType,
4548
string name,

src/Http/Http.Abstractions/src/Validation/TypeExtensions.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,48 @@ public static bool TryGetRequiredAttribute(this ValidationAttribute[] attributes
6666
requiredAttribute = null;
6767
return false;
6868
}
69+
70+
/// <summary>
71+
/// Gets all types that the specified type implements or inherits from, including itself.
72+
/// </summary>
73+
/// <param name="type">The type to analyze.</param>
74+
/// <returns>A collection containing the type itself, all implemented interfaces, and all base types.</returns>
75+
public static IEnumerable<Type> GetAllImplementedTypes([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type)
76+
{
77+
ArgumentNullException.ThrowIfNull(type);
78+
79+
// Yield all interfaces directly and indirectly implemented by this type
80+
foreach (var interfaceType in type.GetInterfaces())
81+
{
82+
yield return interfaceType;
83+
}
84+
85+
// Finally, walk up the inheritance chain
86+
var baseType = type.BaseType;
87+
while (baseType != null && baseType != typeof(object))
88+
{
89+
yield return baseType;
90+
baseType = baseType.BaseType;
91+
}
92+
}
93+
94+
/// <summary>
95+
/// Determines whether the specified type implements the given interface.
96+
/// </summary>
97+
/// <param name="type">The type to check.</param>
98+
/// <param name="interfaceType">The interface type to check for.</param>
99+
/// <returns>True if the type implements the specified interface; otherwise, false.</returns>
100+
public static bool ImplementsInterface(this Type type, Type interfaceType)
101+
{
102+
ArgumentNullException.ThrowIfNull(type);
103+
ArgumentNullException.ThrowIfNull(interfaceType);
104+
105+
// Check if interfaceType is actually an interface
106+
if (!interfaceType.IsInterface)
107+
{
108+
throw new ArgumentException($"Type {interfaceType.FullName} is not an interface.", nameof(interfaceType));
109+
}
110+
111+
return interfaceType.IsAssignableFrom(type);
112+
}
69113
}

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

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.ComponentModel.DataAnnotations;
55
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Linq;
78

89
namespace Microsoft.AspNetCore.Http.Validation;
@@ -13,27 +14,21 @@ namespace Microsoft.AspNetCore.Http.Validation;
1314
public abstract class ValidatableTypeInfo : IValidatableInfo
1415
{
1516
private readonly int _membersCount;
16-
private readonly int _validatableSubtypesCount;
17+
private readonly IEnumerable<Type> _subTypes;
1718

1819
/// <summary>
1920
/// Creates a new instance of <see cref="ValidatableTypeInfo"/>.
2021
/// </summary>
2122
/// <param name="type">The type being validated.</param>
2223
/// <param name="members">The members that can be validated.</param>
23-
/// <param name="implementsIValidatableObject">Indicates whether the type implements IValidatableObject.</param>
24-
/// <param name="validatableSubTypes">The sub-types that can be validated.</param>
2524
public ValidatableTypeInfo(
26-
Type type,
27-
IReadOnlyList<ValidatablePropertyInfo> members,
28-
bool implementsIValidatableObject,
29-
IReadOnlyList<Type>? validatableSubTypes = null)
25+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type,
26+
IReadOnlyList<ValidatablePropertyInfo> members)
3027
{
3128
Type = type;
3229
Members = members;
33-
IsIValidatableObject = implementsIValidatableObject;
34-
ValidatableSubTypes = validatableSubTypes;
3530
_membersCount = members.Count;
36-
_validatableSubtypesCount = validatableSubTypes?.Count ?? 0;
31+
_subTypes = type.GetAllImplementedTypes();
3732
}
3833

3934
/// <summary>
@@ -46,16 +41,6 @@ public ValidatableTypeInfo(
4641
/// </summary>
4742
internal IReadOnlyList<ValidatablePropertyInfo> Members { get; }
4843

49-
/// <summary>
50-
/// The sub-types that can be validated.
51-
/// </summary>
52-
internal IReadOnlyList<Type>? ValidatableSubTypes { get; }
53-
54-
/// <summary>
55-
/// Indicates whether the type implements IValidatableObject.
56-
/// </summary>
57-
internal bool IsIValidatableObject { get; }
58-
5944
/// <summary>
6045
/// Validates the specified value.
6146
/// </summary>
@@ -92,26 +77,22 @@ public virtual async ValueTask ValidateAsync(object? value, ValidatableContext c
9277
}
9378

9479
// Then validate sub-types if any
95-
if (ValidatableSubTypes is not null)
80+
foreach (var subType in _subTypes)
9681
{
97-
for (var i = 0; i < _validatableSubtypesCount; i++)
82+
// Check if the actual type is assignable to the sub-type
83+
// and validate it if it is
84+
if (subType.IsAssignableFrom(actualType))
9885
{
99-
var subType = ValidatableSubTypes[i];
100-
// Check if the actual type is assignable to the sub-type
101-
// and validate it if it is
102-
if (subType.IsAssignableFrom(actualType))
86+
if (context.ValidationOptions.TryGetValidatableTypeInfo(subType, out var subTypeInfo))
10387
{
104-
if (context.ValidationOptions.TryGetValidatableTypeInfo(subType, out var subTypeInfo))
105-
{
106-
await subTypeInfo.ValidateAsync(value, context, cancellationToken);
107-
context.Prefix = originalPrefix;
108-
}
88+
await subTypeInfo.ValidateAsync(value, context, cancellationToken);
89+
context.Prefix = originalPrefix;
10990
}
11091
}
11192
}
11293

11394
// Finally validate IValidatableObject if implemented
114-
if (IsIValidatableObject && value is IValidatableObject validatable)
95+
if (Type.ImplementsInterface(typeof(IValidatableObject)) && value is IValidatableObject validatable)
11596
{
11697
// Important: Set the DisplayName to the type name for top-level validations
11798
// and restore the original validation context properties

src/Http/Http.Abstractions/src/Validation/ValidationOptions.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ public class ValidationOptions
3535
/// <param name="validatableTypeInfo">When this method returns, contains the validation information for the specified type,
3636
/// if the type was found; otherwise, null.</param>
3737
/// <returns>true if validation information was found for the specified type; otherwise, false.</returns>
38-
public bool TryGetValidatableTypeInfo(Type type, [NotNullWhen(true)] out ValidatableTypeInfo? validatableTypeInfo)
38+
public bool TryGetValidatableTypeInfo(Type type, [NotNullWhen(true)] out IValidatableInfo? validatableTypeInfo)
3939
{
4040
foreach (var resolver in Resolvers)
4141
{
42-
validatableTypeInfo = resolver.GetValidatableTypeInfo(type);
43-
if (validatableTypeInfo is not null)
42+
if (resolver.TryGetValidatableTypeInfo(type, out validatableTypeInfo))
4443
{
4544
return true;
4645
}
@@ -54,21 +53,20 @@ public bool TryGetValidatableTypeInfo(Type type, [NotNullWhen(true)] out Validat
5453
/// Attempts to get validation information for the specified parameter.
5554
/// </summary>
5655
/// <param name="parameterInfo">The parameter to get validation information for.</param>
57-
/// <param name="validatableParameterInfo">When this method returns, contains the validation information for the specified parameter,
56+
/// <param name="validatableInfo">When this method returns, contains the validation information for the specified parameter,
5857
/// if validation information was found; otherwise, null.</param>
5958
/// <returns>true if validation information was found for the specified parameter; otherwise, false.</returns>
60-
public bool TryGetValidatableParameterInfo(ParameterInfo parameterInfo, [NotNullWhen(true)] out ValidatableParameterInfo? validatableParameterInfo)
59+
public bool TryGetValidatableParameterInfo(ParameterInfo parameterInfo, [NotNullWhen(true)] out IValidatableInfo? validatableInfo)
6160
{
6261
foreach (var resolver in Resolvers)
6362
{
64-
validatableParameterInfo = resolver.GetValidatableParameterInfo(parameterInfo);
65-
if (validatableParameterInfo is not null)
63+
if (resolver.TryGetValidatableParameterInfo(parameterInfo, out validatableInfo))
6664
{
6765
return true;
6866
}
6967
}
7068

71-
validatableParameterInfo = null;
69+
validatableInfo = null;
7270
return false;
7371
}
7472
}

0 commit comments

Comments
 (0)