Skip to content

Fix for: Minimal APIs and controllers treat header arrays differently #58251

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

Merged
merged 15 commits into from
Mar 4, 2025

Conversation

smnsht
Copy link
Contributor

@smnsht smnsht commented Oct 5, 2024

Fix for: Minimal APIs and controllers treat header arrays differently #54978

Alter BindParameterFromProperty(...) method

Description

The root cause of the issue is in GetValueFromProperty(...) behavior - it returns an Expression that evaluates httpContext.Request.Headers[key]. What we want, is to call httpContext.Request.Headers.GetCommaSeparatedValues(key) when the source is "header" and [FromHeader] is attached to an array.

GetCommaSeparatedValues(key) is an extension method, implemented as ParsingHelpers.GetHeaderSplit(IHeaderDictionary, string)

Fixes #54978

@dotnet-issue-labeler dotnet-issue-labeler bot added the old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels label Oct 5, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Oct 5, 2024
@martincostello martincostello added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels labels Oct 5, 2024
@smnsht smnsht changed the title Fix for: Minimal APIs and controllers treat header arrays differently #55803 Fix for: Minimal APIs and controllers treat header arrays differently #54978 Oct 5, 2024
@smnsht smnsht changed the title Fix for: Minimal APIs and controllers treat header arrays differently #54978 Fix for: Minimal APIs and controllers treat header arrays differently Oct 5, 2024
@smnsht smnsht marked this pull request as ready for review October 5, 2024 11:30
@BrennanConroy
Copy link
Member

There is an active PR for this that has gone through some review and iterations.

If you want to work on this bug you should check with the original PR author and see if they aren't going to work on their fix anymore so that you can take over.

@smnsht
Copy link
Contributor Author

smnsht commented Oct 7, 2024

My bad.

I've asked him just now.

@BrennanConroy
Copy link
Member

Thanks for checking with them, looks like you're good to go.

I'd recommend taking a look at their PR and the reviews that took place to get an idea of what the current change might be missing.

@smnsht
Copy link
Contributor Author

smnsht commented Oct 9, 2024

I'd recommend taking a look at their PR and the reviews that took place to get an idea of what the current change might be missing.

  1. I think a unit test with IEnumerable (other PR) should not be added. This code could not be compiled

dotnet 8.0 - screenshot
image

Confirmation would be appreciated

  1. I see there are changes in RequestDelegateGenerator.cs in other PR. I don't know yet why this change is needed, but I noticed it was explicitly requested by the maintainer. My current understanding is that I should figure this out and properly implement it.

Confirmation would be appreciated

@captainsafia
Copy link
Member

I see there are changes in RequestDelegateGenerator.cs in other PR. I don't know yet why this change is needed, but I noticed it was explicitly requested by the maintainer. My current understanding is that I should figure this out and properly implement it.

The Request Delegate Generator is the source generator implementation that compiles arbitrary delegates to RequestDelegates when you are using native AoT. The behavior between the source generator and non-source generator based implementations should be the same which is why I provided that feedback.

I think a unit test with IEnumerable (other PR) should not be added. This code could not be compiled

This error comes from our analyzers. We'll probably want to update them so they account for the expanded support.

Also, FWIW, I do think it is probably sufficient for us to just support arrays in this binding since that's what we do elsewhere for parameters.

@smnsht
Copy link
Contributor Author

smnsht commented Oct 9, 2024

@captainsafia
Thanks. Going to fix that.

@smnsht smnsht marked this pull request as draft October 9, 2024 18:33
@smnsht smnsht marked this pull request as ready for review October 20, 2024 19:37
@smnsht smnsht requested review from wtgodbe and a team as code owners October 20, 2024 19:37
Copy link
Member

@captainsafia captainsafia left a comment

Choose a reason for hiding this comment

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

Looking good! Left a few comments inline.

Copy link
Member

Choose a reason for hiding this comment

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

You'll want to revert the changes in this submodule before we're able to merge this.

Copy link
Contributor Author

@smnsht smnsht Oct 23, 2024

Choose a reason for hiding this comment

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

[InlineData("""app.MapGet("/", ([FromHeader(Name = "q")] string[]? arr) => arr);""", "", "[]")]
[InlineData("""app.MapGet("/", ([FromHeader(Name = "q")] string[]? arr) => arr);""", "a,b,c", "[\"a\",\"b\",\"c\"]")]
[InlineData("""app.MapGet("/", ([FromHeader(Name = "q")] int[]? arr) => arr);""", "1,2,3", "[1,2,3]")]
public async Task RequestDelegateGenerator_FromHeader_CommaSeparatedValues(string source, string headerContent, string expectedBody)
Copy link
Member

Choose a reason for hiding this comment

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

If you move this test case to RequestDelegateCreationTests.Arrays.cs we'll get coverage for both compile-time and run-time code gen with a single test case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Http.Generated
{
var wasParamCheckFailure = false;
// Endpoint Parameter: p (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], IsOptional = False, IsParsable = True, IsArray = True, Source = Header)
var p_raw = httpContext.Request.Headers["p"];
var p_raw = httpContext.Request.Headers.GetCommaSeparatedValues("p");
Copy link
Member

Choose a reason for hiding this comment

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

Nice use of the extension method here!

BindParameterFromValue(parameter, GetValueFromProperty(property, itemProperty, key, GetExpressionType(parameter.ParameterType)), factoryContext, source);
private static Expression BindParameterFromProperty(ParameterInfo parameter, MemberExpression property, PropertyInfo itemProperty, string key, RequestDelegateFactoryContext factoryContext, string source)
{
var expressionType = GetExpressionType(parameter.ParameterType);
Copy link
Member

Choose a reason for hiding this comment

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

Are string arrays the only type that we need to handle here? Does MVC support serializing int[] from the header for example?

Copy link
Contributor Author

@smnsht smnsht Oct 23, 2024

Choose a reason for hiding this comment

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

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Oct 30, 2024
Copy link
Member

@captainsafia captainsafia left a comment

Choose a reason for hiding this comment

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

Looks good overall! Left one small comment inline that we can use as a way to rekick the stale build for this change.

{
var valueExpression = (source == "header" && parameter.ParameterType.IsArray)
? Expression.Call(
typeof(ParsingHelpers).GetMethod(nameof(ParsingHelpers.GetHeaderSplit), BindingFlags.Public | BindingFlags.Static, [typeof(IHeaderDictionary), typeof(string)])!,
Copy link
Member

Choose a reason for hiding this comment

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

Nit: consider caching this at the top of the file as the other MethodInfo definitions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will do this in February.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MethodInfo caching done

@captainsafia captainsafia requested a review from Copilot January 13, 2025 18:35
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

@captainsafia captainsafia merged commit 99c501c into dotnet:main Mar 4, 2025
27 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the 10.0-preview3 milestone Mar 4, 2025
@smnsht smnsht deleted the 54978 branch March 11, 2025 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc community-contribution Indicates that the PR has been added by a community member pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Minimal APIs and controllers treat header arrays differently
6 participants