Skip to content

Commit 0ee6bca

Browse files
authored
Fix support for parameter names sourced from attributes (dotnet#45572)
1 parent 63d2fb0 commit 0ee6bca

File tree

2 files changed

+30
-15
lines changed

2 files changed

+30
-15
lines changed

Diff for: src/OpenApi/src/OpenApiGenerator.cs

+15-15
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,34 +426,34 @@ 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
{
433433
// Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here.
434434
if (parameter.Name is { } name && pattern.GetParameter(name) is not null)
435435
{
436-
return (false, ParameterLocation.Path);
436+
return (false, ParameterLocation.Path, null);
437437
}
438438
else
439439
{
440-
return (false, ParameterLocation.Query);
440+
return (false, ParameterLocation.Query, null);
441441
}
442442
}
443443
else if (parameter.ParameterType == typeof(IFormFile) || parameter.ParameterType == typeof(IFormFileCollection))
444444
{
445-
return (true, null);
445+
return (true, null, null);
446446
}
447447
else if (disableInferredBody && (
448448
(parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)) ||
449449
parameter.ParameterType == typeof(string[]) ||
450450
parameter.ParameterType == typeof(StringValues)))
451451
{
452-
return (false, ParameterLocation.Query);
452+
return (false, ParameterLocation.Query, null);
453453
}
454454
else
455455
{
456-
return (true, null);
456+
return (true, null, null);
457457
}
458458
}
459459
}

Diff for: src/OpenApi/test/OpenApiGeneratorTests.cs

+15
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)