Skip to content

Commit 5fa89a8

Browse files
authored
Fix support for parameter names sourced from attributes (#45572) (#45591)
1 parent e8dcb27 commit 5fa89a8

File tree

2 files changed

+30
-15
lines changed

2 files changed

+30
-15
lines changed

src/OpenApi/src/OpenApiGenerator.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ private static void GenerateDefaultResponses(Dictionary<int, (Type?, MediaTypeCo
259259
var parameters = PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache);
260260
foreach (var parameter in parameters)
261261
{
262-
var (bodyOrFormParameter, _) = GetOpenApiParameterLocation(parameter, pattern, false);
262+
var (bodyOrFormParameter, _, _) = GetOpenApiParameterLocation(parameter, pattern, false);
263263
hasFormOrBodyParameter |= bodyOrFormParameter;
264264
if (hasFormOrBodyParameter)
265265
{
@@ -368,7 +368,7 @@ private List<OpenApiParameter> GetOpenApiParameters(MethodInfo methodInfo, Route
368368
throw new InvalidOperationException($"Encountered a parameter of type '{parameter.ParameterType}' without a name. Parameters must have a name.");
369369
}
370370

371-
var (_, parameterLocation) = GetOpenApiParameterLocation(parameter, pattern, disableInferredBody);
371+
var (_, parameterLocation, attributeName) = GetOpenApiParameterLocation(parameter, pattern, disableInferredBody);
372372

373373
// if the parameter doesn't have a valid location
374374
// then we should ignore it
@@ -379,7 +379,7 @@ private List<OpenApiParameter> GetOpenApiParameters(MethodInfo methodInfo, Route
379379
var nullabilityContext = new NullabilityInfoContext();
380380
var nullability = nullabilityContext.Create(parameter);
381381
var isOptional = parameter.HasDefaultValue || nullability.ReadState != NullabilityState.NotNull;
382-
var name = pattern.GetParameter(parameter.Name) is { } routeParameter ? routeParameter.Name : parameter.Name;
382+
var name = attributeName ?? (pattern.GetParameter(parameter.Name) is { } routeParameter ? routeParameter.Name : parameter.Name);
383383
var openApiParameter = new OpenApiParameter()
384384
{
385385
Name = name,
@@ -393,29 +393,29 @@ private List<OpenApiParameter> GetOpenApiParameters(MethodInfo methodInfo, Route
393393
return openApiParameters;
394394
}
395395

396-
private (bool isBodyOrForm, ParameterLocation? locatedIn) GetOpenApiParameterLocation(ParameterInfo parameter, RoutePattern pattern, bool disableInferredBody)
396+
private (bool isBodyOrForm, ParameterLocation? locatedIn, string? name) GetOpenApiParameterLocation(ParameterInfo parameter, RoutePattern pattern, bool disableInferredBody)
397397
{
398398
var attributes = parameter.GetCustomAttributes();
399399

400400
if (attributes.OfType<IFromRouteMetadata>().FirstOrDefault() is { } routeAttribute)
401401
{
402-
return (false, ParameterLocation.Path);
402+
return (false, ParameterLocation.Path, routeAttribute.Name);
403403
}
404404
else if (attributes.OfType<IFromQueryMetadata>().FirstOrDefault() is { } queryAttribute)
405405
{
406-
return (false, ParameterLocation.Query);
406+
return (false, ParameterLocation.Query, queryAttribute.Name);
407407
}
408408
else if (attributes.OfType<IFromHeaderMetadata>().FirstOrDefault() is { } headerAttribute)
409409
{
410-
return (false, ParameterLocation.Header);
410+
return (false, ParameterLocation.Header, headerAttribute.Name);
411411
}
412412
else if (attributes.OfType<IFromBodyMetadata>().FirstOrDefault() is { } fromBodyAttribute)
413413
{
414-
return (true, null);
414+
return (true, null, null);
415415
}
416416
else if (attributes.OfType<IFromFormMetadata>().FirstOrDefault() is { } fromFormAttribute)
417417
{
418-
return (true, null);
418+
return (true, null, null);
419419
}
420420
else if (parameter.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType)) ||
421421
parameter.ParameterType == typeof(HttpContext) ||
@@ -426,7 +426,7 @@ private List<OpenApiParameter> GetOpenApiParameters(MethodInfo methodInfo, Route
426426
ParameterBindingMethodCache.HasBindAsyncMethod(parameter) ||
427427
_serviceProviderIsService?.IsService(parameter.ParameterType) == true)
428428
{
429-
return (false, null);
429+
return (false, null, null);
430430
}
431431
else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType))
432432
{
@@ -436,27 +436,27 @@ private List<OpenApiParameter> GetOpenApiParameters(MethodInfo methodInfo, Route
436436
// Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here.
437437
if (parameter.Name is { } name && pattern.GetParameter(name) is not null)
438438
{
439-
return (false, ParameterLocation.Path);
439+
return (false, ParameterLocation.Path, null);
440440
}
441441
else
442442
{
443-
return (false, ParameterLocation.Query);
443+
return (false, ParameterLocation.Query, null);
444444
}
445445
}
446446
else if (parameter.ParameterType == typeof(IFormFile) || parameter.ParameterType == typeof(IFormFileCollection))
447447
{
448-
return (true, null);
448+
return (true, null, null);
449449
}
450450
else if (disableInferredBody && (
451451
(parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)) ||
452452
parameter.ParameterType == typeof(string[]) ||
453453
parameter.ParameterType == typeof(StringValues)))
454454
{
455-
return (false, ParameterLocation.Query);
455+
return (false, ParameterLocation.Query, null);
456456
}
457457
else
458458
{
459-
return (true, null);
459+
return (true, null, null);
460460
}
461461
}
462462
}

src/OpenApi/test/OpenApiGeneratorTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,21 @@ public void HandlesEndpointWithNoRequestBody()
913913
Assert.Null(operationWithNoBodyParams.RequestBody);
914914
}
915915

916+
[Fact]
917+
public void HandlesParameterWithNameInAttribute()
918+
{
919+
static void ValidateParameter(OpenApiOperation operation, string expectedName)
920+
{
921+
var parameter = Assert.Single(operation.Parameters);
922+
Assert.Equal(expectedName, parameter.Name);
923+
}
924+
925+
ValidateParameter(GetOpenApiOperation(([FromRoute(Name = "routeName")] string param) => ""), "routeName");
926+
ValidateParameter(GetOpenApiOperation(([FromRoute(Name = "routeName")] string param) => "", "/{param}"), "routeName");
927+
ValidateParameter(GetOpenApiOperation(([FromQuery(Name = "queryName")] string param) => ""), "queryName");
928+
ValidateParameter(GetOpenApiOperation(([FromHeader(Name = "headerName")] string param) => ""), "headerName");
929+
}
930+
916931
private static OpenApiOperation GetOpenApiOperation(
917932
Delegate action,
918933
string pattern = null,

0 commit comments

Comments
 (0)