Skip to content

Cannot bind StringValues for optional HTTP request header with Minimal APIs #40752

Closed
@martincostello

Description

@martincostello

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

Metadata

Metadata

Assignees

Labels

area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcbugThis issue describes a behavior which is not expected - a bug.feature-minimal-actionsController-like actions for endpoint routingold-area-web-frameworks-do-not-use*DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions