-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Expand FromX support and add route vs. query param inference #46715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
9e15ee2
27a71f6
4a2d762
d0f04e0
e47e69d
99f9af9
c7b6fb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,39 +17,56 @@ public EndpointParameter(IParameterSymbol parameter, WellKnownTypes wellKnownTyp | |
| Type = parameter.Type; | ||
| Name = parameter.Name; | ||
| Source = EndpointParameterSource.Unknown; | ||
| HandlerArgument = $"{parameter.Name}_local"; | ||
|
|
||
| var fromQueryMetadataInterfaceType = wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromQueryMetadata); | ||
| var fromServiceMetadataInterfaceType = wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata); | ||
| var fromRouteMetadataInterfaceType = wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromRouteMetadata); | ||
| var fromHeaderMetadataInterfaceType = wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromHeaderMetadata); | ||
|
|
||
| if (parameter.HasAttributeImplementingInterface(fromQueryMetadataInterfaceType)) | ||
| if (parameter.HasAttributeImplementingInterface(fromRouteMetadataInterfaceType, out var fromRouteAttribute)) | ||
| { | ||
| Source = EndpointParameterSource.Route; | ||
| Name = fromRouteAttribute.TryGetNamedArgumentValue<string>("Name", out var fromRouteName) | ||
| ? fromRouteName | ||
| : parameter.Name; | ||
| IsOptional = parameter.IsOptional(); | ||
| } | ||
| else if (parameter.HasAttributeImplementingInterface(fromQueryMetadataInterfaceType, out var fromQueryAttribute)) | ||
| { | ||
| Source = EndpointParameterSource.Query; | ||
| AssigningCode = $"httpContext.Request.Query[\"{parameter.Name}\"]"; | ||
| IsOptional = parameter.Type is INamedTypeSymbol | ||
| { | ||
| NullableAnnotation: NullableAnnotation.Annotated | ||
| }; | ||
| Name = fromQueryAttribute.TryGetNamedArgumentValue<string>("Name", out var fromQueryName) | ||
| ? fromQueryName | ||
| : parameter.Name; | ||
| IsOptional = parameter.IsOptional(); | ||
| } | ||
| else if (parameter.HasAttributeImplementingInterface(fromHeaderMetadataInterfaceType, out var fromHeaderAttribute)) | ||
| { | ||
| Source = EndpointParameterSource.Header; | ||
| Name = fromHeaderAttribute.TryGetNamedArgumentValue<string>("Name", out var fromHeaderName) | ||
| ? fromHeaderName | ||
| : parameter.Name; | ||
| IsOptional = parameter.IsOptional(); | ||
| } | ||
| else if (TryGetExplicitFromJsonBody(parameter, wellKnownTypes, out var jsonBodyAssigningCode, out var isOptional)) | ||
| else if (TryGetExplicitFromJsonBody(parameter, wellKnownTypes, out var isOptional)) | ||
| { | ||
| Source = EndpointParameterSource.JsonBody; | ||
| AssigningCode = jsonBodyAssigningCode; | ||
| IsOptional = isOptional; | ||
| } | ||
| else if (parameter.HasAttributeImplementingInterface(fromServiceMetadataInterfaceType)) | ||
| { | ||
| Source = EndpointParameterSource.Service; | ||
| IsOptional = parameter.Type is INamedTypeSymbol { NullableAnnotation: NullableAnnotation.Annotated } || parameter.HasExplicitDefaultValue; | ||
| AssigningCode = IsOptional ? | ||
| $"httpContext.RequestServices.GetService<{parameter.Type}>();" : | ||
| $"httpContext.RequestServices.GetRequiredService<{parameter.Type}>()"; | ||
| } | ||
| else if (TryGetSpecialTypeAssigningCode(Type, wellKnownTypes, out var specialTypeAssigningCode)) | ||
| { | ||
| Source = EndpointParameterSource.SpecialType; | ||
| AssigningCode = specialTypeAssigningCode; | ||
| } | ||
| else if (parameter.Type.SpecialType == SpecialType.System_String) | ||
| { | ||
| Source = EndpointParameterSource.RouteOrQuery; | ||
| IsOptional = parameter.IsOptional(); | ||
| } | ||
| else | ||
| { | ||
| // TODO: Inferencing rules go here - but for now: | ||
|
|
@@ -60,19 +77,12 @@ public EndpointParameter(IParameterSymbol parameter, WellKnownTypes wellKnownTyp | |
| public ITypeSymbol Type { get; } | ||
| public EndpointParameterSource Source { get; } | ||
|
|
||
| // TODO: If the parameter has [FromRoute("AnotherName")] or similar, prefer that. | ||
| // Omly used for SpecialType parameters that need | ||
| // to be resolved by a specific WellKnownType | ||
| internal string? AssigningCode { get; set; } | ||
| public string Name { get; } | ||
| public string? AssigningCode { get; } | ||
| public string HandlerArgument { get; } | ||
| public bool IsOptional { get; } | ||
|
|
||
| public string EmitArgument() => Source switch | ||
| { | ||
| EndpointParameterSource.SpecialType or EndpointParameterSource.Query or EndpointParameterSource.Service => HandlerArgument, | ||
| EndpointParameterSource.JsonBody => IsOptional ? HandlerArgument : $"{HandlerArgument}!", | ||
| _ => throw new Exception("Unreachable!") | ||
| }; | ||
|
|
||
| // TODO: Handle special form types like IFormFileCollection that need special body-reading logic. | ||
| private static bool TryGetSpecialTypeAssigningCode(ITypeSymbol type, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out string? callingCode) | ||
| { | ||
|
|
@@ -118,25 +128,16 @@ private static bool TryGetSpecialTypeAssigningCode(ITypeSymbol type, WellKnownTy | |
|
|
||
| private static bool TryGetExplicitFromJsonBody(IParameterSymbol parameter, | ||
| WellKnownTypes wellKnownTypes, | ||
| [NotNullWhen(true)] out string? assigningCode, | ||
| out bool isOptional) | ||
| { | ||
| assigningCode = null; | ||
| isOptional = false; | ||
| if (parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromBodyMetadata), out var fromBodyAttribute)) | ||
| { | ||
| foreach (var namedArgument in fromBodyAttribute.NamedArguments) | ||
| { | ||
| if (namedArgument.Key == "AllowEmpty") | ||
| { | ||
| isOptional |= namedArgument.Value.Value is true; | ||
| } | ||
| } | ||
| isOptional |= (parameter.NullableAnnotation == NullableAnnotation.Annotated || parameter.HasExplicitDefaultValue); | ||
| assigningCode = $"await GeneratedRouteBuilderExtensionsCore.TryResolveBody<{parameter.Type}>(httpContext, {(isOptional ? "true" : "false")})"; | ||
| return true; | ||
| if (!parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromBodyMetadata), out var fromBodyAttribute)) | ||
| { | ||
| return false; | ||
| } | ||
| return false; | ||
| isOptional |= fromBodyAttribute.TryGetNamedArgumentValue<int>("EmptyBodyBehavior", out var emptyBodyBehaviorValue) && emptyBodyBehaviorValue == 1; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this code still handle a custom IFromBodyMetadata attribute when the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was a little iffy on whether or not we should support both. We use the custom I ended up adding it back since we're not in a position to support |
||
| isOptional |= (parameter.NullableAnnotation == NullableAnnotation.Annotated || parameter.HasExplicitDefaultValue); | ||
| return true; | ||
| } | ||
|
|
||
| public override bool Equals(object obj) => | ||
|
|
@@ -145,6 +146,10 @@ obj is EndpointParameter other && | |
| other.Name == Name && | ||
| SymbolEqualityComparer.Default.Equals(other.Type, Type); | ||
|
|
||
| public bool SignatureEquals(object obj) => | ||
| obj is EndpointParameter other && | ||
| SymbolEqualityComparer.Default.Equals(other.Type, Type); | ||
|
|
||
| public override int GetHashCode() | ||
| { | ||
| var hashCode = new HashCode(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.