Closed
Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
If attempting to use the new support for binding StringValues
with Minimal APIs for an HTTP request header that is optional, the endpoint cannot be invoked.
If the header is not present, the endpoint will not be invoked and the client will receive a framework-generated HTTP 400 error.
// Works in a browser, but not from other clients such as PowerShell's Invoke-WebRequest
app.MapGet("/optional-header", ([FromHeader(Name = "Accept")] StringValues accept) => $"You accept {accept}!");
❯ iwr https://localhost:7144/optional-header
Invoke-WebRequest: Microsoft.AspNetCore.Http.BadHttpRequestException: Required parameter "StringValues accept" was not provided from header.
at lambda_method2(Closure, Object, HttpContext)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
HEADERS
=======
Host: localhost:7144
User-Agent: Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.22000; en-GB) PowerShell/7.2.1
Content-Length: 0
If the parameter is made nullable (i.e. StringValues?
), then the application fails to start as the call to MapGet()
throws an InvalidOperationException
.
app.MapGet("/optional-header-2", ([FromHeader(Name = "Accepts")] StringValues? accepts) => $"You accept {accepts}!");
System.InvalidOperationException
HResult=0x80131509
Message=No public static bool Nullable<StringValues>.TryParse(string, out Nullable<StringValues>) method found for accepts.
Source=Microsoft.AspNetCore.Http.Extensions
StackTrace:
at Microsoft.AspNetCore.Http.RequestDelegateFactory.BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, FactoryContext factoryContext, String source)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateArgument(ParameterInfo parameter, FactoryContext factoryContext)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateArguments(ParameterInfo[] parameters, FactoryContext factoryContext)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression targetExpression, FactoryContext factoryContext)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(Delegate handler, RequestDelegateFactoryOptions options)
at Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate handler, Boolean disableInferBodyFromParameters)
at Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapMethods(IEndpointRouteBuilder endpoints, String pattern, IEnumerable`1 httpMethods, Delegate handler)
at Program.<Main>$(String[] args)
Expected Behavior
The endpoint is called with an empty StringValues
and/or StringValues?
can be used as a valid parameter type.
Steps To Reproduce
Use the following Program.cs
with an ASP.NET Core 7 preview 2 web application.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Typically works as clients usually specify User-Agent
app.MapGet("/required-header", ([FromHeader(Name = "User-Agent")] StringValues userAgent) => $"Hello {userAgent}!");
// Works in a browser, but not from other clients such as PowerShell's Invoke-WebRequest
app.MapGet("/optional-header", ([FromHeader(Name = "Accept")] StringValues accept) => $"You accept {accept}!");
// Throws InvalidOperationException - "No public static bool Nullable<StringValues>.TryParse(string, out Nullable<StringValues>) method found for accepts."
app.MapGet("/optional-header-2", ([FromHeader(Name = "Accepts")] StringValues? accepts) => $"You accept {accepts}!");
app.Run();
Exceptions (if any)
System.InvalidOperationException
HResult=0x80131509
Message=No public static bool Nullable<StringValues>.TryParse(string, out Nullable<StringValues>) method found for accepts.
Source=Microsoft.AspNetCore.Http.Extensions
StackTrace:
at Microsoft.AspNetCore.Http.RequestDelegateFactory.BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, FactoryContext factoryContext, String source)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateArgument(ParameterInfo parameter, FactoryContext factoryContext)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateArguments(ParameterInfo[] parameters, FactoryContext factoryContext)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression targetExpression, FactoryContext factoryContext)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(Delegate handler, RequestDelegateFactoryOptions options)
at Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate handler, Boolean disableInferBodyFromParameters)
at Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapMethods(IEndpointRouteBuilder endpoints, String pattern, IEnumerable`1 httpMethods, Delegate handler)
at Program.<Main>$(String[] args)
.NET Version
7.0.100-preview.2.22153.17
Anything else?
No response