Skip to content
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

Expand FromX support and add route vs. query param inference #46715

Merged
merged 7 commits into from
Feb 24, 2023

Conversation

captainsafia
Copy link
Member

Part of #46275.

  • Add support for parameters with FromRoute and FromHeader attribute
    • Includes support for Name argument on attributes
    • Includes support for requiredness checks via nullability and default value checks
  • Add support for Name argument in FromQuery attribute
  • Add support for default value-based requiredness check on FromQuery attributes
  • Update tests to use SharedTypes instead of types in test source
  • Add support for inference for parameters between route and query values

@@ -9,11 +9,11 @@ internal static class EndpointParameterEmitter
internal static string EmitSpecialParameterPreparation(this EndpointParameter endpointParameter)
{
return $"""
var {endpointParameter.Name}_local = {endpointParameter.AssigningCode};
var {endpointParameter.HandlerArgument} = {endpointParameter.AssigningCode};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to get a bit concerned about the tight coupling between EndpointParameterEmitter and EndpointParameter (and friends). It is starting to seem to me like we should just collapse the types together and just have a method on EndpointParameter to emit the block of code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually thinking the other way around. EndpointParameter should be strictly about processing syntax to set state on the parameter objects and EndpointParameterEmitter should handle constructing the blocks of code.

I'll add a commit to showcase what this might look like.

@mkArtakMSFT mkArtakMSFT added the area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels label Feb 20, 2023
@captainsafia
Copy link
Member Author

@dotnet/minimal-apis Can I get a review? 🙏🏽

var updatedSource = @"app.MapGet(""/"", ([FromBody] Todo? todo) => TypedResults.Ok(todo));";
var source = $"""app.MapGet("/", ([{typeof(FromBodyAttribute)}] {typeof(Todo)} todo) => TypedResults.Ok(todo));""";
var updatedSource = $"""
#pragma warning disable CS8622
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my learning, why do we get this warning here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a result of the bug outlined in #46622

Comment on lines 308 to 309
var value_raw = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery(httpContext, "value", options?.RouteParameterNames);
var value_local = value_raw;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need value_local here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is meant to indicate where the TryParse code in #46696 would go. It mostly exists to make rebasing easier since we'll need to support TryParse on parameters from multiple sources.

@@ -87,15 +153,31 @@ internal static string EmitServiceParameterPreparation(this EndpointParameter en
{endpointParameter.EmitParameterDiagnosticComment()}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit on existing code)

Does this need to be wrapped in a """ string? Why can't it just be:

builder.AppendLine(endpointParameter.EmitParameterDiagnosticComment());

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation! 😢

But see #46845 for some goodies.

- Add support for EmpyBodyBehavior check
- Update source snapshots to implicit tests
- Avoid codegenning `TryResolveRouteOrQuery` if not needed
- Clean up test types
Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I just had a few more minor comments.

if (namedArgument.Key == argumentName)
{
var routeParameterNameConstant = namedArgument.Value;
argumentValue = (T)routeParameterNameConstant.Value!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the argument is set to null? Are we sure this will always be non null?

Copy link
Member Author

@captainsafia captainsafia Feb 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good call! Yes, it's possible to do something like [FromQuery(Name = null)] for example. I updated the implementation here and added a guard against this scenario.

Sidenote: I think this is a bug in the RDF, but we'll find out in #46838

}
return false;
isOptional |= fromBodyAttribute.TryGetNamedArgumentValue<int>("EmptyBodyBehavior", out var emptyBodyBehaviorValue) && emptyBodyBehaviorValue == 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this code still handle a custom IFromBodyMetadata attribute when the AllowEmpty property is set in the attribute?

Copy link
Member Author

Choose a reason for hiding this comment

The 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 FromBodyAttribute implementation in our RDF tests (ref) but I don't think this is a common pattern in user code.

I ended up adding it back since we're not in a position to support FromBody vs. FromService inference and might need to implement a FromBody attribute in our own source to avoid importing the Mvc namespace.

- Guard parameter name from attribute against null values
- Support FromBody with AllowEmpty named argument
@captainsafia
Copy link
Member Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants