Skip to content
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

Show DisplayName for conflicting actions #5057

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ public static JsonSerializerOptions GetSystemTextJsonSettings(IServiceProvider s
var options = serviceProvider.GetService(optionsType);
var value = optionsType.GetProperty("Value")?.GetValue(options);
var jsonOptions = value?.GetType().GetProperty("JsonSerializerOptions")?.GetValue(value);
if (jsonOptions is JsonSerializerOptions)
if (jsonOptions is JsonSerializerOptions serializerOptions)
{
return (JsonSerializerOptions)jsonOptions;
return serializerOptions;
}
}
catch
Expand Down Expand Up @@ -336,7 +336,12 @@ private List<Tuple<OpenApiOperationDescription, ApiDescription, MethodInfo>> Add

if (pathItem.ContainsKey(operation.Method))
{
throw new InvalidOperationException($"The method '{operation.Method}' on path '{path}' is registered multiple times.");
var conflictingApiDescriptions = operations
.Where(t => t.Item1.Path == operation.Path && t.Item1.Method == operation.Method)
.Select(t => t.Item2)
.ToList();

throw new InvalidOperationException($"The method '{operation.Method}' on path '{path}' is registered multiple times for actions {string.Join(", ", conflictingApiDescriptions.Select(apiDesc => apiDesc.ActionDescriptor.DisplayName))}.");
}

pathItem[operation.Method] = operation.Operation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ private static void EnsureSingleBodyParameter(OpenApiOperationDescription operat
{
if (operationDescription.Operation.ActualParameters.Count(p => p.Kind == OpenApiParameterKind.Body) > 1)
{
throw new InvalidOperationException("The operation '" + operationDescription.Operation.OperationId + "' has more than one body parameter.");
throw new InvalidOperationException($"The operation '{operationDescription.Operation.OperationId}' has more than one body parameter.");
}
}

Expand Down
34 changes: 22 additions & 12 deletions src/NSwag.Generation.WebApi/WebApiOpenApiDocumentGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,26 +214,36 @@ private bool AddOperationDescriptionsToDocument(OpenApiDocument document, Type c
{
var path = operation.Path.Replace("//", "/");

if (!document.Paths.TryGetValue(path, out OpenApiPathItem value))
if (!document.Paths.TryGetValue(path, out var pathItem))
{
value = [];
document.Paths[path] = value;
pathItem = [];
document.Paths[path] = pathItem;
}

if (value.ContainsKey(operation.Method))
if (pathItem.ContainsKey(operation.Method))
{
throw new InvalidOperationException("The method '" + operation.Method + "' on path '" + path + "' is registered multiple times " +
var conflictingOperationDisplayNames = operations
.Where(t => t.Item1.Path == operation.Path && t.Item1.Method == operation.Method)
.Select(t => GetDisplayName(controllerType, t.Item2))
.ToList();

throw new InvalidOperationException($"The method '{operation.Method}' on path '{path}' is registered multiple times for action {string.Join(", ", conflictingOperationDisplayNames)} " +
"(check the DefaultUrlTemplate setting [default for Web API: 'api/{controller}/{id}'; for MVC projects: '{controller}/{action}/{id?}']).");
}

value[operation.Method] = operation.Operation;
pathItem[operation.Method] = operation.Operation;
addedOperations++;
}
}

return addedOperations > 0;
}

private static string GetDisplayName(Type controllerType, MethodInfo method)
{
return $"{controllerType.FullName}.{method.Name} ({controllerType.Assembly.GetName().Name})";
}

private bool RunOperationProcessors(OpenApiDocument document, Type controllerType, MethodInfo methodInfo, OpenApiOperationDescription operationDescription,
List<OpenApiOperationDescription> allOperations, OpenApiDocumentGenerator generator, OpenApiSchemaResolver schemaResolver)
{
Expand Down Expand Up @@ -326,11 +336,11 @@ private string GetOperationId(OpenApiDocument document, string controllerName, M
controllerName = controllerName.Substring(0, controllerName.Length - 10);
}

operationId = controllerName + "_" + GetActionName(method);
operationId = $"{controllerName}_{GetActionName(method)}";
}

var number = 1;
while (document.Operations.Any(o => o.Operation.OperationId == operationId + (number > 1 ? "_" + number : string.Empty)))
while (document.Operations.Any(o => o.Operation.OperationId == operationId + (number > 1 ? $"_{number}" : string.Empty)))
{
number++;
}
Expand Down Expand Up @@ -359,7 +369,7 @@ private List<string> GetHttpPaths(Type controllerType, MethodInfo method)
}
else if (routePrefixAttribute != null)
{
httpPaths.Add(routePrefixAttribute.Prefix + "/" + attribute.Template);
httpPaths.Add($"{routePrefixAttribute.Prefix}/{attribute.Template}");
}
else if (routeAttributesOnClass != null)
{
Expand All @@ -371,7 +381,7 @@ private List<string> GetHttpPaths(Type controllerType, MethodInfo method)
{
foreach (var routeAttributeOnClass in routeAttributesOnClass)
{
httpPaths.Add(routeAttributeOnClass.Template + "/" + attribute.Template);
httpPaths.Add($"{routeAttributeOnClass.Template}/{attribute.Template}");
}
}
}
Expand All @@ -385,7 +395,7 @@ private List<string> GetHttpPaths(Type controllerType, MethodInfo method)
{
foreach (var routeAttributeOnClass in routeAttributesOnClass)
{
httpPaths.Add(routePrefixAttribute.Prefix + "/" + routeAttributeOnClass.Template);
httpPaths.Add($"{routePrefixAttribute.Prefix}/{routeAttributeOnClass.Template}");
}
}
else if (routePrefixAttribute != null)
Expand Down Expand Up @@ -422,7 +432,7 @@ private List<string> GetHttpPaths(Type controllerType, MethodInfo method)
private static IEnumerable<string> ExpandOptionalHttpPathParameters(string path, MethodInfo method)
{
var segments = path.Split(['/'], StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < segments.Length; i++)
for (var i = 0; i < segments.Length; i++)
{
var segment = segments[i];
if (segment.EndsWith("?}", StringComparison.Ordinal))
Expand Down
52 changes: 26 additions & 26 deletions src/NSwag.sln
Original file line number Diff line number Diff line change
Expand Up @@ -475,30 +475,6 @@ Global
{AC3D8125-AE21-49FC-A217-D96C7B585FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC3D8125-AE21-49FC-A217-D96C7B585FF9}.Release|x64.ActiveCfg = Release|Any CPU
{AC3D8125-AE21-49FC-A217-D96C7B585FF9}.Release|x86.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x64.ActiveCfg = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x64.Build.0 = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x86.ActiveCfg = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x86.Build.0 = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|Any CPU.Build.0 = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x64.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x64.Build.0 = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x86.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x86.Build.0 = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x64.ActiveCfg = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x64.Build.0 = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x86.ActiveCfg = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x86.Build.0 = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|Any CPU.Build.0 = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x64.ActiveCfg = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x64.Build.0 = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x86.ActiveCfg = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x86.Build.0 = Release|Any CPU
{CF6112E5-20FD-4B22-A6C0-20AF6B3396F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF6112E5-20FD-4B22-A6C0-20AF6B3396F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF6112E5-20FD-4B22-A6C0-20AF6B3396F3}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -535,6 +511,30 @@ Global
{F0569608-BD55-4316-94F0-E85A14D7FE14}.Release|x64.Build.0 = Release|Any CPU
{F0569608-BD55-4316-94F0-E85A14D7FE14}.Release|x86.ActiveCfg = Release|Any CPU
{F0569608-BD55-4316-94F0-E85A14D7FE14}.Release|x86.Build.0 = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x64.ActiveCfg = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x64.Build.0 = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x86.ActiveCfg = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Debug|x86.Build.0 = Debug|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|Any CPU.Build.0 = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x64.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x64.Build.0 = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x86.ActiveCfg = Release|Any CPU
{DE82965A-6935-43E0-A9A1-F3F35B4487EB}.Release|x86.Build.0 = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x64.ActiveCfg = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x64.Build.0 = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x86.ActiveCfg = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Debug|x86.Build.0 = Debug|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|Any CPU.Build.0 = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x64.ActiveCfg = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x64.Build.0 = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x86.ActiveCfg = Release|Any CPU
{24693FBC-445E-4360-A1E8-B6F136C563FB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -573,11 +573,11 @@ Global
{B6467D5A-7B13-4B06-89BE-2BC7B6615956} = {1030C3AA-7549-473B-AA7E-5DFAC2F9D37A}
{F237A592-2D74-4C38-B222-2C91029B87F8} = {48E8B044-3374-4F39-A180-9E01F7A10785}
{AC3D8125-AE21-49FC-A217-D96C7B585FF9} = {6F5E4FDF-0A82-42D5-94AC-A9CD43CC931D}
{DE82965A-6935-43E0-A9A1-F3F35B4487EB} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5}
{24693FBC-445E-4360-A1E8-B6F136C563FB} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5}
{CF6112E5-20FD-4B22-A6C0-20AF6B3396F3} = {F0F26A35-C4B6-42D0-A1DF-98CA46A5C560}
{2A166077-2189-4376-A38B-8E362A319028} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5}
{F0569608-BD55-4316-94F0-E85A14D7FE14} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5}
{DE82965A-6935-43E0-A9A1-F3F35B4487EB} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5}
{24693FBC-445E-4360-A1E8-B6F136C563FB} = {D8CC0D1C-8DAC-49FE-AA78-C028DC124DD5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {98FCEEE2-A45C-41E7-B2ED-1B14755E9067}
Expand Down
Loading