From 92e03adcc29b9e9682a10b35765df53266b16feb Mon Sep 17 00:00:00 2001 From: Ao Rui Date: Fri, 23 Jan 2026 17:20:33 +0800 Subject: [PATCH 1/3] add Swashbuckle.AspNetCore.SwaggerGen 10.0 and .NET 10 support. --- Unchase.Swashbuckle.AspNetCore.Extensions.sln | 20 +- .../Extensions/EnumTypeExtensions.cs | 54 +++-- .../Extensions/OpenApiDocumentExtensions.cs | 4 +- .../Extensions/SwaggerGenOptionsExtensions.cs | 21 +- .../Extensions/XmlCommentsExtensions.cs | 10 +- .../Factories/ApiDescriptionFactory.cs | 62 +++--- ...ndActionCountToTagSummaryDocumentFilter.cs | 2 +- ...eResponseByHttpStatusCodeDocumentFilter.cs | 53 ++--- .../DisplayEnumsWithValuesDocumentFilter.cs | 24 +- ...athsAndDefinitionsByRolesDocumentFilter.cs | 165 +++++++------- .../Filters/InheritDocOperationFilter.cs | 10 +- .../Filters/InheritDocParameterFilter.cs | 6 +- .../Filters/InheritDocRequestBodyFilter.cs | 6 +- .../Filters/InheritDocSchemaFilter.cs | 4 +- .../Filters/TagOrderByNameDocumentFilter.cs | 4 +- .../Filters/XEnumNamesParameterFilter.cs | 114 ++++++---- .../Filters/XEnumNamesSchemaFilter.cs | 80 ++++--- .../XmlCommentsWithRemarksDocumentFilter.cs | 16 +- .../XmlCommentsWithRemarksParameterFilter.cs | 10 +- ...XmlCommentsWithRemarksRequestBodyFilter.cs | 8 +- .../XmlCommentsWithRemarksSchemaFilter.cs | 12 +- .../Options/FixEnumsOptions.cs | 8 +- ...e.Swashbuckle.AspNetCore.Extensions.csproj | 13 +- ...hase.Swashbuckle.AspNetCore.Extensions.xml | 80 +++---- ...hbuckle.AspNetCore.Extensions.Tests.csproj | 6 +- test/WebApi3.1-Swashbuckle/Program.cs | 22 -- .../Properties/launchSettings.json | 29 --- test/WebApi3.1-Swashbuckle/Startup.cs | 202 ----------------- .../WebApi3.1-Swashbuckle.csproj | 20 -- .../WebApi3.1-Swashbuckle.xml | 8 - .../appsettings.Development.json | 9 - test/WebApi3.1-Swashbuckle/appsettings.json | 10 - .../Controllers/RecordsController.cs | 34 --- .../Models/RecordResponse.cs | 12 - test/WebApi5-Swashbuckle/Program.cs | 22 -- .../Properties/launchSettings.json | 31 --- test/WebApi5-Swashbuckle/Startup.cs | 206 ------------------ .../WebApi5-Swashbuckle.csproj | 20 -- .../WebApi5-Swashbuckle.xml | 47 ---- .../appsettings.Development.json | 9 - test/WebApi5-Swashbuckle/appsettings.json | 10 - .../Controllers/RecordsController.cs | 0 .../Models/RecordResponse.cs | 0 .../Program.cs | 8 +- .../Properties/launchSettings.json | 2 +- .../WebApi8-Swashbuckle.csproj} | 4 +- .../appsettings.Development.json | 0 .../appsettings.json | 0 48 files changed, 434 insertions(+), 1063 deletions(-) delete mode 100644 test/WebApi3.1-Swashbuckle/Program.cs delete mode 100644 test/WebApi3.1-Swashbuckle/Properties/launchSettings.json delete mode 100644 test/WebApi3.1-Swashbuckle/Startup.cs delete mode 100644 test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.csproj delete mode 100644 test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.xml delete mode 100644 test/WebApi3.1-Swashbuckle/appsettings.Development.json delete mode 100644 test/WebApi3.1-Swashbuckle/appsettings.json delete mode 100644 test/WebApi5-Swashbuckle/Controllers/RecordsController.cs delete mode 100644 test/WebApi5-Swashbuckle/Models/RecordResponse.cs delete mode 100644 test/WebApi5-Swashbuckle/Program.cs delete mode 100644 test/WebApi5-Swashbuckle/Properties/launchSettings.json delete mode 100644 test/WebApi5-Swashbuckle/Startup.cs delete mode 100644 test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.csproj delete mode 100644 test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.xml delete mode 100644 test/WebApi5-Swashbuckle/appsettings.Development.json delete mode 100644 test/WebApi5-Swashbuckle/appsettings.json rename test/{WebApi7-Swashbuckle => WebApi8-Swashbuckle}/Controllers/RecordsController.cs (100%) rename test/{WebApi7-Swashbuckle => WebApi8-Swashbuckle}/Models/RecordResponse.cs (100%) rename test/{WebApi7-Swashbuckle => WebApi8-Swashbuckle}/Program.cs (98%) rename test/{WebApi7-Swashbuckle => WebApi8-Swashbuckle}/Properties/launchSettings.json (96%) rename test/{WebApi7-Swashbuckle/WebApi7-Swashbuckle.csproj => WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj} (79%) rename test/{WebApi7-Swashbuckle => WebApi8-Swashbuckle}/appsettings.Development.json (100%) rename test/{WebApi7-Swashbuckle => WebApi8-Swashbuckle}/appsettings.json (100%) diff --git a/Unchase.Swashbuckle.AspNetCore.Extensions.sln b/Unchase.Swashbuckle.AspNetCore.Extensions.sln index 31b2d6b..5791df9 100644 --- a/Unchase.Swashbuckle.AspNetCore.Extensions.sln +++ b/Unchase.Swashbuckle.AspNetCore.Extensions.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33122.133 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7C4518F9-6506-49BE-B39D-1BD1BE121E4B}" EndProject @@ -32,11 +32,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.Swashbuckle.AspNetC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.Swashbuckle.AspNetCore.Extensions.Tests", "test\Unchase.Swashbuckle.AspNetCore.Extensions.Tests\Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj", "{ADFE3232-3F94-4582-BE4E-CD93603EAE18}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi3.1-Swashbuckle", "test\WebApi3.1-Swashbuckle\WebApi3.1-Swashbuckle.csproj", "{1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi5-Swashbuckle", "test\WebApi5-Swashbuckle\WebApi5-Swashbuckle.csproj", "{C9251364-9941-4776-A9A3-841173DB58C4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi7-Swashbuckle", "test\WebApi7-Swashbuckle\WebApi7-Swashbuckle.csproj", "{4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi8-Swashbuckle", "test\WebApi8-Swashbuckle\WebApi8-Swashbuckle.csproj", "{4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -52,14 +48,6 @@ Global {ADFE3232-3F94-4582-BE4E-CD93603EAE18}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADFE3232-3F94-4582-BE4E-CD93603EAE18}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADFE3232-3F94-4582-BE4E-CD93603EAE18}.Release|Any CPU.Build.0 = Release|Any CPU - {1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Release|Any CPU.Build.0 = Release|Any CPU - {C9251364-9941-4776-A9A3-841173DB58C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9251364-9941-4776-A9A3-841173DB58C4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9251364-9941-4776-A9A3-841173DB58C4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9251364-9941-4776-A9A3-841173DB58C4}.Release|Any CPU.Build.0 = Release|Any CPU {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -71,8 +59,6 @@ Global GlobalSection(NestedProjects) = preSolution {63011319-4764-44DE-B8C2-CDABAD5FD8BE} = {7C4518F9-6506-49BE-B39D-1BD1BE121E4B} {ADFE3232-3F94-4582-BE4E-CD93603EAE18} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} - {1AD5C40F-8AF7-4DAA-9C72-40D1269304C0} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} - {C9251364-9941-4776-A9A3-841173DB58C4} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs index fb7bcdb..3dcaee1 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs @@ -6,9 +6,10 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; +using System.Text.Json.Nodes; +using System.Text.Json; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions { @@ -55,7 +56,13 @@ private static string GetFieldAttributeDescription( return string.Empty; } - var memInfo = enumType.GetMember(enumField.ToString()); + var enumFieldString = enumField.ToString(); + if (string.IsNullOrWhiteSpace(enumFieldString)) + { + return string.Empty; + } + + var memInfo = enumType.GetMember(enumFieldString); var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), true); if (attributes.Length > 0) { @@ -65,13 +72,13 @@ private static string GetFieldAttributeDescription( return string.Empty; } - internal static List<(object EnumValue, OpenApiString EnumDescription)> GetEnumValuesDescription( + internal static List<(object EnumValue, JsonNode EnumDescription)> GetEnumValuesDescription( Type enumType, DescriptionSources descriptionSource, IEnumerable xmlNavigators, bool includeRemarks = false) { - var enumsDescriptions = new List<(object, OpenApiString)>(); + var enumsDescriptions = new List<(object, JsonNode)>(); foreach (var enumValue in Enum.GetValues(enumType)) { var enumDescription = string.Empty; @@ -106,11 +113,11 @@ private static string GetFieldAttributeDescription( { if (!string.IsNullOrWhiteSpace(enumDescription)) { - enumsDescriptions.Add((enumValue, new OpenApiString(enumDescription))); + enumsDescriptions.Add((enumValue, enumDescription)); } else { - enumsDescriptions.Add((enumValue, new OpenApiString(string.Empty))); + enumsDescriptions.Add((enumValue, string.Empty)); } } } @@ -119,16 +126,17 @@ private static string GetFieldAttributeDescription( } private static string TryGetMemberComments( - MemberInfo memberInfo, + MemberInfo? memberInfo, IEnumerable xmlNavigators, bool includeRemarks = false) { - var commentsBuilder = new StringBuilder(); - if (xmlNavigators == null) + if (memberInfo == null || xmlNavigators == null) { return string.Empty; } + var commentsBuilder = new StringBuilder(); + foreach (var xmlNavigator in xmlNavigators) { var nodeNameForMember = GetNodeNameForMember(memberInfo); @@ -158,7 +166,7 @@ private static string GetNodeNameForMember( MemberInfo memberInfo) { var stringBuilder = new StringBuilder((memberInfo.MemberType & MemberTypes.Field) != 0 ? "F:" : "P:"); - stringBuilder.Append(QualifiedNameFor(memberInfo.DeclaringType, false)); + stringBuilder.Append(QualifiedNameFor(memberInfo.DeclaringType!, false)); stringBuilder.Append("." + memberInfo.Name); return stringBuilder.ToString(); } @@ -169,7 +177,7 @@ private static string QualifiedNameFor( { if (type.IsArray) { - return QualifiedNameFor(type.GetElementType(), expandGenericArgs) + "[]"; + return QualifiedNameFor(type.GetElementType()!, expandGenericArgs) + "[]"; } var stringBuilder = new StringBuilder(); @@ -199,20 +207,20 @@ private static string QualifiedNameFor( return stringBuilder.ToString(); } - internal static string AddEnumValuesDescription( - this OpenApiSchema schema, + internal static string? AddEnumValuesDescription( + this IOpenApiSchema schema, string xEnumNamesAlias, string xEnumDescriptionsAlias, bool includeDescriptionFromAttribute = false, string newLine = "\n") { - if (schema.Enum == null || schema.Enum.Count == 0) + if (schema.Extensions == null || schema.Enum == null || schema.Enum.Count == 0) { return null; } if (!schema.Extensions.ContainsKey(xEnumNamesAlias) || - ((OpenApiArray)schema.Extensions[xEnumNamesAlias]).Count != schema.Enum.Count) + ((JsonArray)((JsonNodeExtension)schema.Extensions[xEnumNamesAlias]).Node).Count != schema.Enum.Count) { return null; } @@ -220,15 +228,15 @@ internal static string AddEnumValuesDescription( var sb = new StringBuilder(); for (var i = 0; i < schema.Enum.Count; i++) { - if (schema.Enum[i] is OpenApiInteger schemaEnumInt) + if (schema.Enum[i] is JsonValue schemaEnumInt && schemaEnumInt.GetValueKind() is JsonValueKind.Number) { - var value = schemaEnumInt.Value; - var name = ((OpenApiString)((OpenApiArray)schema.Extensions[xEnumNamesAlias])[i]).Value; + var value = schemaEnumInt.GetValue(); + var name = ((JsonArray)((JsonNodeExtension)schema.Extensions[xEnumNamesAlias]).Node)[i]!.GetValue(); sb.Append($"{newLine}{newLine}{value} = {name}"); } - else if (schema.Enum[i] is OpenApiString schemaEnumString) + else if (schema.Enum[i] is JsonValue schemaEnumString && schemaEnumString.GetValueKind() is JsonValueKind.String) { - var value = schemaEnumString.Value; + var value = schemaEnumString.GetValue(); sb.Append($"{newLine}{newLine}{value}"); } @@ -240,10 +248,10 @@ internal static string AddEnumValuesDescription( continue; } - var xEnumDescriptions = (OpenApiArray)schema.Extensions[xEnumDescriptionsAlias]; + var xEnumDescriptions = (JsonArray)((JsonNodeExtension)schema.Extensions[xEnumDescriptionsAlias]).Node; if (xEnumDescriptions?.Count == schema.Enum.Count) { - var description = ((OpenApiString)((OpenApiArray)schema.Extensions[xEnumDescriptionsAlias])[i]).Value; + var description = xEnumDescriptions[i]!.GetValue(); if (!string.IsNullOrWhiteSpace(description)) { sb.Append($" ({description})"); diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs index 74e91c9..71fb2e2 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs @@ -4,7 +4,7 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Unchase.Swashbuckle.AspNetCore.Extensions.Factories; using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; @@ -32,7 +32,7 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesFor acceptedRoles) where TController : class, new() { - var actionDescriptor = ApiDescriptionFactory.Create(actionNameSelector, typeof(TController).GetCustomAttribute().Template)?.ActionDescriptor; + var actionDescriptor = ApiDescriptionFactory.Create(actionNameSelector, typeof(TController).GetCustomAttribute()?.Template)?.ActionDescriptor; if (actionDescriptor != null) { var paths = new Dictionary<(MethodInfo, Type), string> diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs index 4b9358d..0fce9c9 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs @@ -20,7 +20,8 @@ public static class SwaggerGenOptionsExtensions #region Fields private static readonly FieldInfo _xmlDocMembers = - typeof(XmlCommentsSchemaFilter).GetField("_xmlDocMembers", BindingFlags.Instance | BindingFlags.NonPublic); + typeof(XmlCommentsSchemaFilter).GetField("_xmlDocMembers", BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException($"Get field info \"_xmlDocMembers\" of type {typeof(XmlCommentsSchemaFilter).FullName} failed."); #endregion @@ -41,9 +42,9 @@ public static class SwaggerGenOptionsExtensions public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode( this SwaggerGenOptions swaggerGenOptions, int httpStatusCode, - string responseDescription = null, + string? responseDescription = null, ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None, - T responseExample = default) where T : class + T? responseExample = default) where T : class { swaggerGenOptions.DocumentFilter>(httpStatusCode, responseDescription, responseExampleOption, responseExample); return swaggerGenOptions; @@ -64,9 +65,9 @@ public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode( public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode( this SwaggerGenOptions swaggerGenOptions, HttpStatusCode httpStatusCode, - string responseDescription = null, + string? responseDescription = null, ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None, - T responseExample = default) where T : class + T? responseExample = default) where T : class { return swaggerGenOptions.ChangeAllResponsesByHttpStatusCode((int)httpStatusCode, responseDescription, responseExampleOption, responseExample); } @@ -78,7 +79,7 @@ public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode( /// An to configure options for filters. public static SwaggerGenOptions AddEnumsWithValuesFixFilters( this SwaggerGenOptions swaggerGenOptions, - Action configureOptions = null) + Action? configureOptions = null) { // local function void EmptyAction(FixEnumsOptions x) { } @@ -155,9 +156,9 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks( this SwaggerGenOptions swaggerGenOptions, Func xmlDocFactory, bool includeControllerXmlComments = false, - Func excludedTypesFunc = default) + Func? excludedTypesFunc = default) { - return swaggerGenOptions.IncludeXmlCommentsWithRemarks(xmlDocFactory, includeControllerXmlComments, excludedTypesFunc?.Invoke()); + return swaggerGenOptions.IncludeXmlCommentsWithRemarks(xmlDocFactory, includeControllerXmlComments, excludedTypesFunc?.Invoke() ?? []); } /// @@ -174,9 +175,9 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks( this SwaggerGenOptions swaggerGenOptions, string filePath, bool includeControllerXmlComments = false, - Func excludedTypesFunc = default) + Func? excludedTypesFunc = default) { - return swaggerGenOptions.IncludeXmlCommentsWithRemarks(() => new XPathDocument(filePath), includeControllerXmlComments, excludedTypesFunc?.Invoke()); + return swaggerGenOptions.IncludeXmlCommentsWithRemarks(() => new XPathDocument(filePath), includeControllerXmlComments, excludedTypesFunc?.Invoke() ?? []); } /// diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs index 6d35504..a202ea9 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs @@ -4,8 +4,7 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions @@ -139,7 +138,7 @@ private static MemberInfo[] GetTargets(MemberInfo memberInfo, string cref) } internal static void ApplyPropertyComments( - this OpenApiSchema schema, + this IOpenApiSchema schema, MemberInfo memberInfo, List documents, Dictionary inheritedDocs, @@ -204,7 +203,10 @@ internal static void ApplyPropertyComments( var exampleNode = targetXmlNode.SelectSingleNode(ExampleTag); if (exampleNode != null) { - schema.Example = new OpenApiString(XmlCommentsTextHelper.Humanize(exampleNode.InnerXml)); + if (schema is OpenApiSchema openApiSchema) + { + openApiSchema.Example = XmlCommentsTextHelper.Humanize(exampleNode.InnerXml); + } } } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs index dd2b47e..065f3ce 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs @@ -32,15 +32,15 @@ internal static class ApiDescriptionFactory /// /// Returns . /// - internal static ApiDescription Create( + internal static ApiDescription? Create( Type controllerType, MethodInfo methodInfo, - string groupName = null, - string httpMethod = null, - string relativePath = null, - IEnumerable parameterDescriptions = null, - IEnumerable supportedRequestFormats = null, - IEnumerable supportedResponseTypes = null) + string? groupName = null, + string? httpMethod = null, + string? relativePath = null, + IEnumerable? parameterDescriptions = null, + IEnumerable? supportedRequestFormats = null, + IEnumerable? supportedResponseTypes = null) { if (methodInfo == null) { @@ -138,21 +138,25 @@ internal static ApiDescription Create( /// /// Returns . /// - internal static ApiDescription Create( + internal static ApiDescription? Create( Type controllerType, string actionName, - string groupName = null, - string httpMethod = null, - string relativePath = null, - IEnumerable parameterDescriptions = null, - IEnumerable supportedRequestFormats = null, - IEnumerable supportedResponseTypes = null) + string? groupName = null, + string? httpMethod = null, + string? relativePath = null, + IEnumerable? parameterDescriptions = null, + IEnumerable? supportedRequestFormats = null, + IEnumerable? supportedResponseTypes = null) { - MethodInfo methodInfo; + MethodInfo? methodInfo; try { methodInfo = controllerType.GetMethod(actionName); + if (methodInfo == null) + { + return null; + } } catch (AmbiguousMatchException) { @@ -177,14 +181,14 @@ internal static ApiDescription Create( /// /// Returns . /// - internal static ApiDescription Create( + internal static ApiDescription? Create( Func actionNameSelector, - string groupName = null, - string httpMethod = null, - string relativePath = null, - IEnumerable parameterDescriptions = null, - IEnumerable supportedRequestFormats = null, - IEnumerable supportedResponseTypes = null) + string? groupName = null, + string? httpMethod = null, + string? relativePath = null, + IEnumerable? parameterDescriptions = null, + IEnumerable? supportedRequestFormats = null, + IEnumerable? supportedResponseTypes = null) where TController : new() { return Create( @@ -213,14 +217,14 @@ internal static ApiDescription Create( /// /// Returns . /// - internal static ApiDescription Create( + internal static ApiDescription? Create( MethodInfo methodInfo, - string groupName = null, - string httpMethod = null, - string relativePath = null, - IEnumerable parameterDescriptions = null, - IEnumerable supportedRequestFormats = null, - IEnumerable supportedResponseTypes = null) + string? groupName = null, + string? httpMethod = null, + string? relativePath = null, + IEnumerable? parameterDescriptions = null, + IEnumerable? supportedRequestFormats = null, + IEnumerable? supportedResponseTypes = null) where TController : class { return Create( diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs index af6c39d..ee159aa 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs index 8b52395..8a73d42 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs @@ -1,7 +1,7 @@ -using System.Linq; - -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -37,6 +37,12 @@ internal class ChangeResponseByHttpStatusCodeDocumentFilter : { #region Fileds + private static JsonSerializerOptions _jsonSerializerOptions = new() + { + WriteIndented = true, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + private readonly int _httpStatusCode; private readonly string _responseDescription; @@ -84,7 +90,7 @@ public void Apply( if (context.SchemaRepository.Schemas.ContainsKey(typeof(T).Name)) { var schema = context.SchemaRepository.Schemas[typeof(T).Name]; - foreach (var response in swaggerDoc.Paths.SelectMany(p => p.Value.Operations.SelectMany(o => o.Value.Responses))) + foreach (var response in swaggerDoc.Paths.SelectMany(p => (p.Value.Operations ?? []).SelectMany(o => o.Value.Responses ?? []))) { if (response.Key == _httpStatusCode.ToString()) { @@ -93,9 +99,8 @@ public void Apply( response.Value.Description = _responseDescription; } - if (response.Value.Content.ContainsKey("application/json")) + if (response.Value.Content != null && response.Value.Content.TryGetValue("application/json", out OpenApiMediaType? jsonContent) && jsonContent != null) { - var jsonContent = response.Value.Content["application/json"]; switch (_responseExampleOption) { case ResponseExampleOptions.Clear: @@ -104,24 +109,17 @@ public void Apply( case ResponseExampleOptions.AddNew: if (_responseExample != null) { - var jsonExample = new OpenApiString(System.Text.Json.JsonSerializer.Serialize(_responseExample, + var jsonExample = System.Text.Json.JsonSerializer.Serialize(_responseExample, new System.Text.Json.JsonSerializerOptions { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - })); + }); jsonContent.Example = jsonExample; } - jsonContent.Schema = new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = typeof(T).Name, - Type = ReferenceType.Schema - } - }; + jsonContent.Schema = new OpenApiSchemaReference(typeof(T).Name, swaggerDoc); break; case ResponseExampleOptions.None: default: @@ -135,23 +133,14 @@ public void Apply( case ResponseExampleOptions.AddNew: if (_responseExample != null) { - var jsonExample = new OpenApiString(System.Text.Json.JsonSerializer.Serialize(_responseExample, - new System.Text.Json.JsonSerializerOptions - { - WriteIndented = true, - Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - })); - response.Value.Content.Add("application/json", new OpenApiMediaType + var jsonExample = JsonSerializer.Serialize(_responseExample, _jsonSerializerOptions); + + var responseValue = response.Value as OpenApiResponse; + responseValue?.Content ??= new Dictionary(); + responseValue?.Content?.Add("application/json", new OpenApiMediaType { Example = jsonExample, - Schema = new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = typeof(T).Name, - Type = ReferenceType.Schema - } - } + Schema = new OpenApiSchemaReference(typeof(T).Name, swaggerDoc) }); } break; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs index 4d38405..3a3f0b1 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs @@ -2,7 +2,7 @@ using System.Linq; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -31,7 +31,7 @@ internal class DisplayEnumsWithValuesDocumentFilter : /// An to configure options for filter. public DisplayEnumsWithValuesDocumentFilter( IOptions options, - Action configureOptions = null) + Action? configureOptions = null) { if (options.Value != null) { @@ -42,6 +42,10 @@ public DisplayEnumsWithValuesDocumentFilter( _xEnumDescriptionsAlias = options.Value.XEnumDescriptionsAlias; _newLine = options.Value.NewLine; } + + _xEnumNamesAlias ??= FixEnumsOptions.DefaultXEnumNamesAlias; + _xEnumDescriptionsAlias ??= FixEnumsOptions.DefaultXEnumDescriptionsAlias; + _newLine ??= "\n"; } #endregion @@ -87,12 +91,12 @@ public void Apply( // add enum descriptions to input parameters of every operation foreach (var parameter in openApiDoc.Paths.Values.SelectMany(v => v.Operations).SelectMany(op => op.Value.Parameters)) { - OpenApiSchema schema = null; - if (parameter.Schema?.Reference == null) + IOpenApiSchema schema = null; + if ((parameter.Schema as OpenApiSchemaReference)?.Reference == null) { if (parameter.Schema?.AllOf?.Count > 0) { - schema = context.SchemaRepository.Schemas.FirstOrDefault(s => parameter.Schema.AllOf.FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; + schema = context.SchemaRepository.Schemas.FirstOrDefault(s => parameter.Schema.AllOf.OfType().FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; } else { @@ -101,7 +105,7 @@ public void Apply( } else { - var componentReference = parameter.Schema?.Reference?.Id; + var componentReference = (parameter.Schema as OpenApiSchemaReference).Reference.Id; if (!string.IsNullOrWhiteSpace(componentReference)) { schema = openApiDoc.Components.Schemas[componentReference]; @@ -133,13 +137,13 @@ public void Apply( { foreach (var requestBodyContent in requestBodyContents) { - if (requestBodyContent.Value.Schema?.Reference?.Id != null) + if (requestBodyContent.Value.Schema is OpenApiSchemaReference requestBodyContentSchemaReference && requestBodyContentSchemaReference.Reference.Id != null) { - var schema = context.SchemaRepository.Schemas[requestBodyContent.Value.Schema?.Reference?.Id]; + var schema = context.SchemaRepository.Schemas[requestBodyContentSchemaReference.Reference.Id] as OpenApiSchema; if (schema != null) { - requestBodyContent.Value.Schema.Description = schema.Description; - requestBodyContent.Value.Schema.Extensions = schema.Extensions; + requestBodyContentSchemaReference.Description = schema.Description; + //requestBodyContentSchemaReference.Extensions = schema.Extensions; } } } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs index 174a47a..432af38 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -40,78 +40,80 @@ public HidePathsAndDefinitionsByRolesDocumentFilter( #region GetRequiredDefinitions private static List GetRequiredDefinitions( - IDictionary schemas, - OpenApiReference reference) + IDictionary schemas, + JsonSchemaReference reference) { - var result = new List(); + var result = new List(); if (reference?.Id != null) { - if (!result.Contains(reference?.Id)) + if (!result.Contains(reference.Id)) { result.Add(reference.Id); } - var responseSchema = schemas[reference?.Id]; - if (responseSchema != null) + if (schemas.TryGetValue(reference?.Id!, out var responseSchema) && responseSchema != null) { if (responseSchema.Properties?.Count > 0) { foreach (var schemaProperty in responseSchema.Properties) { - if (schemaProperty.Value?.Reference?.Id != null) + if (schemaProperty.Value is OpenApiSchemaReference schemaPropertyReference && schemaPropertyReference.Reference?.Id != null) { - result.Add(schemaProperty.Value?.Reference?.Id); - var responseSchemaPropertySchema = schemas[schemaProperty.Value?.Reference?.Id]; - if (responseSchemaPropertySchema?.Reference != null) + result.Add(schemaPropertyReference.Reference.Id); + var responseSchemaPropertySchema = schemas[schemaPropertyReference.Reference.Id]; + if (responseSchemaPropertySchema is OpenApiSchemaReference responseSchemaPropertySchemaReference && responseSchemaPropertySchemaReference.Reference != null) { - result.AddRange(GetRequiredDefinitions(schemas, responseSchemaPropertySchema.Reference)); - if (responseSchemaPropertySchema.Items?.Reference?.Id != null) + result.AddRange(GetRequiredDefinitions(schemas, responseSchemaPropertySchemaReference.Reference)); + if (responseSchemaPropertySchema.Items is OpenApiSchemaReference responseSchemaPropertySchemaItemReference && responseSchemaPropertySchemaItemReference.Reference.Id != null) { - var itemsSchema = schemas[responseSchemaPropertySchema.Items?.Reference?.Id]; - if (itemsSchema?.Reference?.Id != null) + var itemsSchema = schemas[responseSchemaPropertySchemaItemReference.Reference.Id]; + if (itemsSchema is OpenApiSchemaReference itemsSchemaReference && itemsSchemaReference.Reference.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, itemsSchema.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaReference.Reference)); } } } else if (responseSchemaPropertySchema != null) { - result.AddRange(GetRequiredDefinitions(schemas, schemaProperty.Value?.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, schemaPropertyReference.Reference)); } } - if (schemaProperty.Value?.Items?.Reference?.Id != null) + if (schemaProperty.Value?.Items is OpenApiSchemaReference schemaPropertyItemReference && schemaPropertyItemReference.Reference?.Id != null) { - result.Add(schemaProperty.Value?.Items?.Reference?.Id); - var itemsSchema = schemas[schemaProperty.Value?.Items?.Reference?.Id]; - if (itemsSchema?.Reference?.Id != null) + result.Add(schemaPropertyItemReference.Reference.Id); + var itemsSchema = schemas[schemaPropertyItemReference.Reference.Id]; + if (itemsSchema is OpenApiSchemaReference itemsSchemaReference && itemsSchemaReference.Reference.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, itemsSchema.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaReference.Reference)); } if (itemsSchema?.Properties?.Count > 0) { foreach (var itemsSchemaProperty in itemsSchema.Properties) { - if (itemsSchemaProperty.Value?.Reference?.Id != null) + var itemsSchemaPropertyReference = itemsSchemaProperty.Value as OpenApiSchemaReference; + if (itemsSchemaPropertyReference?.Reference?.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaProperty.Value?.Reference)); - result.Add(itemsSchemaProperty.Value?.Reference?.Id); + result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaPropertyReference.Reference)); + result.Add(itemsSchemaPropertyReference?.Reference?.Id); } - if (itemsSchemaProperty.Value?.Items?.Reference?.Id != null) + var itemsSchemaPropertyReferenceItems = itemsSchemaPropertyReference?.Items as OpenApiSchemaReference; + if (itemsSchemaPropertyReferenceItems?.Reference?.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaProperty.Value?.Items?.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaPropertyReferenceItems.Reference)); } if (itemsSchemaProperty.Value?.AllOf?.Count > 0) { - foreach (var itemsSchemaPropertyAllOf in itemsSchemaProperty.Value?.AllOf) + foreach (var itemsSchemaPropertyAllOf in itemsSchemaProperty.Value.AllOf) { - if (itemsSchemaPropertyAllOf?.Reference?.Id != null) + var itemsSchemaPropertyAllOfReference = itemsSchemaPropertyAllOf as OpenApiSchemaReference; + if (itemsSchemaPropertyAllOfReference?.Reference?.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaPropertyAllOf?.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, itemsSchemaPropertyAllOfReference.Reference)); } } } @@ -121,32 +123,32 @@ private static List GetRequiredDefinitions( if (schemaProperty.Value?.AllOf?.Count > 0) { - foreach (var schemaPropertyAllOf in schemaProperty.Value?.AllOf) + foreach (var schemaPropertyAllOf in schemaProperty.Value.AllOf) { - if (schemaPropertyAllOf?.Reference?.Id != null) + var schemaPropertyAllOfReference = schemaPropertyAllOf as OpenApiSchemaReference; + if (schemaPropertyAllOfReference?.Reference?.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, schemaPropertyAllOf.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, schemaPropertyAllOfReference.Reference)); } } } } } - if (responseSchema?.Items?.Reference?.Id != null) + var responseSchemaItems = responseSchema?.Items as OpenApiSchemaReference; + if (responseSchemaItems?.Reference?.Id != null) { - result.Add(responseSchema?.Items?.Reference?.Id); - var itemsSchema = schemas[responseSchema?.Items?.Reference?.Id]; + result.Add(responseSchemaItems.Reference.Id); + var itemsSchema = schemas[responseSchemaItems.Reference.Id] as OpenApiSchemaReference; if (itemsSchema?.Reference?.Id != null) { - result.AddRange(GetRequiredDefinitions(schemas, itemsSchema?.Reference)); + result.AddRange(GetRequiredDefinitions(schemas, itemsSchema.Reference)); } } } } - result = result.Distinct().ToList(); - - return result; + return result.Distinct().Where(static x => x != null).ToList()!; } #endregion @@ -163,7 +165,7 @@ private static List GetRequiredDefinitions( internal static void RemovePathsAndComponents( OpenApiDocument openApiDoc, IDictionary<(MethodInfo ActionMethodInfo, Type ControllerType), string> paths, - IDictionary schemas, + IDictionary schemas, IReadOnlyList acceptedRoles) { var keysForRemove = new List(); @@ -205,32 +207,34 @@ internal static void RemovePathsAndComponents( #region Remove Components - foreach (var operation in openApiDoc?.Paths?.Where(p => p.Value?.Operations != null)?.SelectMany(p => p.Value?.Operations)) + foreach (var operation in openApiDoc.Paths?.Where(p => p.Value.Operations != null)?.SelectMany(p => p.Value.Operations!) ?? []) { if (operation.Value?.Responses != null) { - foreach (var response in operation.Value?.Responses) + foreach (var response in operation.Value.Responses) { - if (response.Value?.Reference?.Id != null && !requiredRefs.Contains(response.Value?.Reference?.Id)) + var responseValue = response.Value as OpenApiSchemaReference; + if (responseValue?.Reference?.Id != null && !requiredRefs.Contains(responseValue.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, response.Value?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, responseValue.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } - foreach (var responseContentSchema in response.Value?.Content?.Select(rc => rc.Value?.Schema)) + foreach (var responseContentSchema in response.Value?.Content?.Select(rc => rc.Value.Schema) ?? []) { - foreach (var responseContentSchemaAllOfItemSchema in responseContentSchema.AllOf.Where(s => s.Reference?.Id != null)) + foreach (var responseContentSchemaAllOfItemSchema in responseContentSchema?.AllOf?.OfType().Where(s => s.Reference.Id != null) ?? []) { - if (responseContentSchemaAllOfItemSchema?.Reference?.Id != null && !requiredRefs.Contains(responseContentSchemaAllOfItemSchema?.Reference?.Id)) + if (responseContentSchemaAllOfItemSchema.Reference.Id != null && !requiredRefs.Contains(responseContentSchemaAllOfItemSchema.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, responseContentSchemaAllOfItemSchema?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, responseContentSchemaAllOfItemSchema.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } } - if (responseContentSchema?.Reference?.Id != null && !requiredRefs.Contains(responseContentSchema?.Reference?.Id)) + var responseContentSchemaRef = response.Value as OpenApiSchemaReference; + if (responseContentSchemaRef?.Reference?.Id != null && !requiredRefs.Contains(responseContentSchemaRef.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, responseContentSchema?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, responseContentSchemaRef.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } } @@ -240,57 +244,60 @@ internal static void RemovePathsAndComponents( if (operation.Value?.RequestBody != null) { var operationRequestBody = operation.Value?.RequestBody; - foreach (var operationRequestBodyContentSchema in operationRequestBody?.Content?.Select(rc => rc.Value?.Schema)) + foreach (var operationRequestBodyContentSchema in operationRequestBody?.Content?.Select(rc => rc.Value.Schema) ?? []) { - foreach (var operationRequestBodyContentSchemaAllOfItemSchema in operationRequestBodyContentSchema.AllOf.Where(s => s.Reference?.Id != null)) + foreach (var operationRequestBodyContentSchemaAllOfItemSchema in operationRequestBodyContentSchema?.AllOf?.OfType().Where(s => s.Reference?.Id != null) ?? []) { - if (operationRequestBodyContentSchemaAllOfItemSchema?.Reference?.Id != null && !requiredRefs.Contains(operationRequestBodyContentSchemaAllOfItemSchema?.Reference?.Id)) + if (operationRequestBodyContentSchemaAllOfItemSchema?.Reference?.Id != null && !requiredRefs.Contains(operationRequestBodyContentSchemaAllOfItemSchema.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, operationRequestBodyContentSchemaAllOfItemSchema?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, operationRequestBodyContentSchemaAllOfItemSchema.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } } - if (operationRequestBodyContentSchema?.Reference?.Id != null && !requiredRefs.Contains(operationRequestBodyContentSchema?.Reference?.Id)) + var operationRequestBodyContentSchemaRef = operationRequestBodyContentSchema as OpenApiSchemaReference; + if (operationRequestBodyContentSchemaRef?.Reference?.Id != null && !requiredRefs.Contains(operationRequestBodyContentSchemaRef.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, operationRequestBodyContentSchema?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, operationRequestBodyContentSchemaRef.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } } } - foreach (var parameter in openApiDoc.Paths?.Where(p => p.Value?.Parameters != null).SelectMany(p => p.Value?.Operations?.SelectMany(o => o.Value?.Parameters))) + foreach (var parameter in openApiDoc.Paths?.Where(p => p.Value.Parameters != null).SelectMany(p => p.Value.Operations?.SelectMany(o => o.Value.Parameters ?? []) ?? []) ?? []) { - if (parameter?.Schema?.Reference?.Id != null && !requiredRefs.Contains(parameter.Schema.Reference.Id)) + var parameterSchemaRef = parameter.Schema as OpenApiSchemaReference; + if (parameterSchemaRef?.Reference?.Id != null && !requiredRefs.Contains(parameterSchemaRef.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, parameter.Schema.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, parameterSchemaRef.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } - foreach (var parameterContentSchema in parameter?.Content?.Select(rc => rc.Value?.Schema)) + foreach (var parameterContentSchema in parameter?.Content?.Select(rc => rc.Value.Schema) ?? []) { - foreach (var parameterContentSchemaAllOfItemSchema in parameterContentSchema.AllOf.Where(s => s.Reference?.Id != null)) + foreach (var parameterContentSchemaAllOfItemSchema in parameterContentSchema?.AllOf?.OfType().Where(s => s.Reference.Id != null) ?? []) { - if (parameterContentSchemaAllOfItemSchema?.Reference?.Id != null && !requiredRefs.Contains(parameterContentSchemaAllOfItemSchema?.Reference?.Id)) + if (parameterContentSchemaAllOfItemSchema.Reference.Id != null && !requiredRefs.Contains(parameterContentSchemaAllOfItemSchema.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, parameterContentSchemaAllOfItemSchema?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, parameterContentSchemaAllOfItemSchema.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } } - if (parameterContentSchema?.Reference?.Id != null && !requiredRefs.Contains(parameterContentSchema?.Reference?.Id)) + var parameterContentSchemaRef = parameterContentSchema as OpenApiSchemaReference; + if (parameterContentSchemaRef?.Reference?.Id != null && !requiredRefs.Contains(parameterContentSchemaRef.Reference.Id)) { - requiredRefs.AddRange(GetRequiredDefinitions(schemas, parameterContentSchema?.Reference)); + requiredRefs.AddRange(GetRequiredDefinitions(schemas, parameterContentSchemaRef.Reference)); requiredRefs = requiredRefs.Distinct().ToList(); } } } } - var definitionsToDelete = openApiDoc.Components.Schemas.Select(d => d.Key).Except(requiredRefs).ToList(); - foreach (var definitionToDelete in definitionsToDelete) + var definitionsToDelete = openApiDoc.Components?.Schemas?.Select(d => d.Key).Except(requiredRefs).ToList(); + foreach (var definitionToDelete in definitionsToDelete ?? []) { - openApiDoc.Components.Schemas.Remove(definitionToDelete); + openApiDoc.Components!.Schemas!.Remove(definitionToDelete); } #endregion @@ -298,23 +305,23 @@ internal static void RemovePathsAndComponents( #region Remove Tags var tagsDict = new Dictionary(); - foreach (var openApiDocTag in openApiDoc.Tags) + foreach (var openApiDocTag in openApiDoc.Tags ?? Enumerable.Empty()) { - if (!tagsDict.ContainsKey(openApiDocTag.Name)) + if (!string.IsNullOrEmpty(openApiDocTag.Name)) { - tagsDict.Add(openApiDocTag.Name, 0); + tagsDict.TryAdd(openApiDocTag.Name, 0); } } - foreach (var operation in openApiDoc.Paths?.Where(p => p.Value?.Operations != null).SelectMany(p => p.Value?.Operations)) + foreach (var operation in openApiDoc.Paths?.Where(p => p.Value.Operations != null).SelectMany(p => p.Value.Operations!) ?? []) { if (operation.Value?.Tags?.Count > 0) { - foreach (var operationTag in operation.Value?.Tags) + foreach (var operationTag in operation.Value.Tags) { - if (operationTag != null && tagsDict.ContainsKey(operationTag.Name)) + if (!string.IsNullOrEmpty(operationTag.Name) && tagsDict.TryGetValue(operationTag.Name, out int value)) { - tagsDict[operationTag.Name]++; + tagsDict[operationTag.Name] = ++value; } } } @@ -324,10 +331,10 @@ internal static void RemovePathsAndComponents( { if (tagsDict[tag] == 0) { - var swaggerTag = openApiDoc.Tags.FirstOrDefault(t => t.Name == tag); + var swaggerTag = openApiDoc.Tags?.FirstOrDefault(t => t.Name == tag); if (swaggerTag != null) { - openApiDoc.Tags.Remove(swaggerTag); + openApiDoc.Tags!.Remove(swaggerTag); } } } @@ -359,7 +366,7 @@ public void Apply( var paths = new Dictionary<(MethodInfo, Type), string>(); foreach (var actionDescriptor in apiDescriptions.Select(ad => ad.ActionDescriptor)) { - var t = ((ControllerActionDescriptor) actionDescriptor).ControllerTypeInfo.AsType(); + var t = ((ControllerActionDescriptor)actionDescriptor).ControllerTypeInfo.AsType(); paths.Add((((ControllerActionDescriptor)actionDescriptor).MethodInfo, t), actionDescriptor.AttributeRouteInfo.Template); } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs index 7144917..9badc5c 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; @@ -174,12 +174,12 @@ public void Apply( continue; } - OpenApiSchema schema = null; - if (parameter.Schema?.Reference == null) + IOpenApiSchema schema = null; + if ((parameter.Schema as OpenApiSchemaReference)?.Reference == null) { if (parameter.Schema?.AllOf?.Count > 0) { - schema = context.SchemaRepository.Schemas.FirstOrDefault(s => parameter.Schema.AllOf.FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; + schema = context.SchemaRepository.Schemas.FirstOrDefault(s => parameter.Schema.AllOf.OfType().FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; } else { @@ -197,7 +197,7 @@ public void Apply( } else { - var componentReference = parameter.Schema?.Reference?.Id; + var componentReference = (parameter.Schema as OpenApiSchemaReference)?.Reference?.Id; if (!string.IsNullOrWhiteSpace(componentReference)) { schema = context.SchemaRepository.Schemas[componentReference]; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs index 315bd69..8927f07 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs @@ -4,7 +4,7 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; @@ -74,7 +74,7 @@ public InheritDocParameterFilter( /// . /// . public void Apply( - OpenApiParameter parameter, + IOpenApiParameter parameter, ParameterFilterContext context) { if (context.ApiParameterDescription?.PropertyInfo() == null) @@ -145,7 +145,7 @@ public void Apply( // Add the summary and examples for the properties. foreach (var entry in schema.Properties) { - var members = ((TypeInfo)type).GetMembers(); + var members = ((TypeInfo)type!).GetMembers(); var memberInfo = members.FirstOrDefault(p => p.Name.Equals(entry.Key, StringComparison.OrdinalIgnoreCase)); if (memberInfo != null) diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs index 458b5cc..945139d 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; @@ -68,7 +68,7 @@ public InheritDocRequestBodyFilter( #region Methods public void Apply( - OpenApiRequestBody requestBody, + IOpenApiRequestBody requestBody, RequestBodyFilterContext context) { ApplyForType(requestBody, context, context.BodyParameterDescription?.Type); @@ -83,7 +83,7 @@ public void Apply( } private void ApplyForType( - OpenApiRequestBody requestBody, + IOpenApiRequestBody requestBody, RequestBodyFilterContext context, Type type) { diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs index 35f0634..daddce3 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; @@ -73,7 +73,7 @@ public InheritDocSchemaFilter( /// . /// . public void Apply( - OpenApiSchema schema, + IOpenApiSchema schema, SchemaFilterContext context) { if (_excludedTypes.Any() && _excludedTypes.ToList().Contains(context.Type)) diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs index 6993ee8..39a1f85 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs @@ -1,6 +1,6 @@ using System.Linq; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -22,7 +22,7 @@ public void Apply( OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Tags = swaggerDoc.Tags.OrderBy(t => t.Name).ToList(); + swaggerDoc.Tags = swaggerDoc.Tags.OrderBy(t => t.Name).ToHashSet(); } #endregion diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs index 866f06d..f29b2dc 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json.Nodes; using System.Xml.XPath; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -39,20 +39,13 @@ internal class XEnumNamesParameterFilter : /// An to configure options for filter. public XEnumNamesParameterFilter( IOptions options, - Action configureOptions = null) + Action? configureOptions = null) { if (options.Value != null) { configureOptions?.Invoke(options.Value); - _includeXEnumDescriptions = options.Value?.IncludeDescriptions ?? false; - _includeXEnumRemarks = options.Value?.IncludeXEnumRemarks ?? false; - _descriptionSources = options.Value?.DescriptionSource ?? DescriptionSources.DescriptionAttributes; - _applyFiler = options.Value?.ApplyParameterFilter ?? false; - _xEnumNamesAlias = options.Value?.XEnumNamesAlias; - _xEnumDescriptionsAlias = options.Value?.XEnumDescriptionsAlias; - _includeDescriptionFromAttribute = options.Value.IncludeDescriptions; - _newLine = options.Value.NewLine; - foreach (var filePath in options.Value?.IncludedXmlCommentsPaths ?? new HashSet()) + + foreach (var filePath in options.Value.IncludedXmlCommentsPaths ?? []) { if (File.Exists(filePath)) { @@ -60,6 +53,15 @@ public XEnumNamesParameterFilter( } } } + + _includeXEnumDescriptions = options.Value?.IncludeDescriptions ?? false; + _includeXEnumRemarks = options.Value?.IncludeXEnumRemarks ?? false; + _descriptionSources = options.Value?.DescriptionSource ?? DescriptionSources.DescriptionAttributes; + _applyFiler = options.Value?.ApplyParameterFilter ?? false; + _xEnumNamesAlias = options.Value?.XEnumNamesAlias ?? FixEnumsOptions.DefaultXEnumNamesAlias; + _xEnumDescriptionsAlias = options.Value?.XEnumDescriptionsAlias ?? FixEnumsOptions.DefaultXEnumDescriptionsAlias; + _includeDescriptionFromAttribute = options.Value?.IncludeDescriptions ?? false; + _newLine = options.Value?.NewLine ?? "\n"; } #endregion @@ -72,7 +74,7 @@ public XEnumNamesParameterFilter( /// . /// . public void Apply( - OpenApiParameter parameter, + IOpenApiParameter parameter, ParameterFilterContext context) { if (!_applyFiler) @@ -86,35 +88,53 @@ public void Apply( return; } - var enumsArray = new OpenApiArray(); - var enumsDescriptionsArray = new OpenApiArray(); + if (parameter is OpenApiParameter apiParameter) + { + apiParameter.Extensions ??= new Dictionary(); + } + else + { + return; + } + + var enumsArray = new JsonArray(); + var enumsDescriptionsArray = new JsonArray(); if (typeInfo.IsEnum) { var names = Enum .GetNames(typeInfo) - .Select(name => (Enum.Parse(typeInfo, name), new OpenApiString(name))) + .Select(name => (Enum.Parse(typeInfo, name), JsonValue.Create(name))) .GroupBy(x => x.Item1) .Select(x => x.LastOrDefault().Item2) .ToList(); - enumsArray.AddRange(names); - if (!parameter.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) + foreach (var name in names) + { + enumsArray.Add(name); + } + + if (parameter.Extensions?.ContainsKey(_xEnumNamesAlias) is false && enumsArray.Any()) { - parameter.Extensions.Add(_xEnumNamesAlias, enumsArray); + parameter.Extensions.Add(_xEnumNamesAlias, new JsonNodeExtension(enumsArray)); } if (_includeXEnumDescriptions) { - enumsDescriptionsArray.AddRange(EnumTypeExtensions + var enumsDescriptions = EnumTypeExtensions .GetEnumValuesDescription(typeInfo, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); - if (!parameter.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) + .Select(x => x.LastOrDefault().EnumDescription); + foreach (var enumsDescription in enumsDescriptions) { - parameter.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); + enumsDescriptionsArray.Add(enumsDescription); + } + + if (parameter.Extensions?.ContainsKey(_xEnumDescriptionsAlias) is false && enumsDescriptionsArray.Any()) + { + parameter.Extensions.Add(_xEnumDescriptionsAlias, new JsonNodeExtension(enumsDescriptionsArray)); } } } - else if (typeInfo.IsGenericType && !parameter.Extensions.ContainsKey(_xEnumNamesAlias)) + else if (typeInfo.IsGenericType && parameter.Extensions?.ContainsKey(_xEnumNamesAlias) is false) { foreach (var genericArgumentType in typeInfo.GetGenericArguments()) { @@ -122,41 +142,54 @@ public void Apply( { var names = Enum .GetNames(genericArgumentType) - .Select(name => (Enum.Parse(genericArgumentType, name), new OpenApiString(name))) + .Select(name => (Enum.Parse(genericArgumentType, name), JsonValue.Create(name))) .GroupBy(x => x.Item1) .Select(x => x.LastOrDefault().Item2) .ToList(); - enumsArray.AddRange(names); + foreach (var name in names) + { + enumsArray.Add(name); + } + if (!parameter.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) { - parameter.Extensions.Add(_xEnumNamesAlias, enumsArray); + parameter.Extensions.Add(_xEnumNamesAlias, new JsonNodeExtension(enumsArray)); } if (_includeXEnumDescriptions) { - enumsDescriptionsArray.AddRange(EnumTypeExtensions + var enumsDescriptions = EnumTypeExtensions .GetEnumValuesDescription(genericArgumentType, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); + .Select(x => x.LastOrDefault().EnumDescription); + foreach (var enumsDescription in enumsDescriptions) + { + enumsDescriptionsArray.Add(enumsDescription); + } + if (!parameter.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) { - parameter.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); + parameter.Extensions.Add(_xEnumDescriptionsAlias, new JsonNodeExtension(enumsDescriptionsArray)); } } } else if (genericArgumentType.IsArray && genericArgumentType.GetElementType()?.IsEnum == true) { - var enumSchema = context.SchemaRepository.Schemas[parameter.Schema.Items.Items.Reference.Id]; - var description = enumSchema.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); - if (description != null) + var parameterEnumSchemaReference = parameter.Schema?.Items?.Items as OpenApiSchemaReference; + if (parameterEnumSchemaReference != null) { - if (parameter.Description == null) - { - parameter.Description = description; - } - else if (!parameter.Description.Contains(description)) + var enumSchema = context.SchemaRepository.Schemas[parameterEnumSchemaReference.Reference.Id]; + var description = enumSchema.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); + if (description != null) { - parameter.Description += description; + if (parameter.Description == null) + { + parameter.Description = description; + } + else if (!parameter.Description.Contains(description)) + { + parameter.Description += description; + } } } } @@ -164,7 +197,8 @@ public void Apply( } else if (typeInfo.IsArray && typeInfo.GetElementType()?.IsEnum == true) { - var enumSchema = context.SchemaRepository.Schemas[parameter.Schema.Items.Reference.Id]; + var parameterEnumSchemaReference = parameter.Schema?.Items as OpenApiSchemaReference; + var enumSchema = context.SchemaRepository.Schemas[parameterEnumSchemaReference.Reference.Id]; var description = enumSchema.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); if (description != null) { diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs index bab25a2..a289380 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs @@ -3,11 +3,11 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Text.Json.Nodes; using System.Xml.XPath; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -38,7 +38,7 @@ internal class XEnumNamesSchemaFilter : ISchemaFilter /// An to configure options for filter. public XEnumNamesSchemaFilter( IOptions options, - Action configureOptions = null) + Action? configureOptions = null) { if (options.Value != null) { @@ -47,9 +47,7 @@ public XEnumNamesSchemaFilter( _includeXEnumRemarks = options.Value.IncludeXEnumRemarks; _descriptionSources = options.Value.DescriptionSource; _applyFiler = options.Value.ApplySchemaFilter; - _xEnumNamesAlias = options.Value.XEnumNamesAlias; - _xEnumDescriptionsAlias = options.Value.XEnumDescriptionsAlias; - _newLine = options.Value.NewLine; + foreach (var filePath in options.Value.IncludedXmlCommentsPaths) { if (File.Exists(filePath)) @@ -58,6 +56,10 @@ public XEnumNamesSchemaFilter( } } } + + _xEnumNamesAlias = options.Value?.XEnumNamesAlias ?? FixEnumsOptions.DefaultXEnumNamesAlias; + _xEnumDescriptionsAlias = options.Value?.XEnumDescriptionsAlias ?? FixEnumsOptions.DefaultXEnumDescriptionsAlias; + _newLine = options.Value?.NewLine ?? "\n"; } #endregion @@ -70,7 +72,7 @@ public XEnumNamesSchemaFilter( /// . /// . public void Apply( - OpenApiSchema schema, + IOpenApiSchema schema, SchemaFilterContext context) { if (!_applyFiler) @@ -79,31 +81,41 @@ public void Apply( } var typeInfo = context.Type.GetTypeInfo(); - var enumsArray = new OpenApiArray(); - var enumsDescriptionsArray = new OpenApiArray(); + var enumsArray = new JsonArray(); + var enumsDescriptionsArray = new JsonArray(); if (typeInfo.IsEnum) { var names = Enum .GetNames(context.Type) - .Select(name => (Enum.Parse(context.Type, name), new OpenApiString(name))) + .Select(name => (Enum.Parse(context.Type, name), JsonValue.Create(name))) .GroupBy(x => x.Item1) .Select(x => x.LastOrDefault().Item2) .ToList(); - enumsArray.AddRange(names); - if (!schema.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) + foreach (var name in names) { - schema.Extensions.Add(_xEnumNamesAlias, enumsArray); + enumsArray.Add(name); + } + + (schema as OpenApiSchema)?.Extensions ??= new Dictionary(); + if (schema.Extensions?.ContainsKey(_xEnumNamesAlias) is false && enumsArray.Any()) + { + schema.Extensions.Add(_xEnumNamesAlias, new JsonNodeExtension(enumsArray)); } if (_includeXEnumDescriptions) { - enumsDescriptionsArray.AddRange(EnumTypeExtensions + var enumsDescriptions = EnumTypeExtensions .GetEnumValuesDescription(context.Type, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); - if (!schema.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) + .Select(x => x.LastOrDefault().EnumDescription); + foreach (var enumDescription in enumsDescriptions) + { + enumsDescriptionsArray.Add(enumDescription); + } + + if (schema.Extensions?.ContainsKey(_xEnumDescriptionsAlias) is false && enumsDescriptionsArray.Any()) { - schema.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); + schema.Extensions.Add(_xEnumDescriptionsAlias, new JsonNodeExtension(enumsDescriptionsArray)); } } @@ -111,7 +123,8 @@ public void Apply( } // add "x-enumNames" or its alias for schema with generic types - if (typeInfo.IsGenericType && !schema.Extensions.ContainsKey(_xEnumNamesAlias)) + (schema as OpenApiSchema)?.Extensions ??= new Dictionary(); + if (typeInfo.IsGenericType && schema.Extensions?.ContainsKey(_xEnumNamesAlias) is false) { foreach (var genericArgumentType in typeInfo.GetGenericArguments()) { @@ -122,30 +135,41 @@ public void Apply( foreach (var schemaProperty in schema.Properties) { var schemaPropertyValue = schemaProperty.Value; - var propertySchema = context.SchemaRepository.Schemas.FirstOrDefault(s => schemaPropertyValue.AllOf.FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; + var propertySchema = context.SchemaRepository.Schemas.FirstOrDefault(s => schemaPropertyValue.AllOf?.OfType().FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; if (propertySchema != null) { var names = Enum .GetNames(genericArgumentType) - .Select(name => (Enum.Parse(genericArgumentType, name), new OpenApiString(name))) + .Select(name => (Enum.Parse(genericArgumentType, name), JsonValue.Create(name))) .GroupBy(x => x.Item1) .Select(x => x.LastOrDefault().Item2) .ToList(); - enumsArray.AddRange(names); - if (!schemaPropertyValue.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) + foreach (var name in names) + { + enumsArray.Add(name); + } + + if (enumsArray.Any()) { - schemaPropertyValue.Extensions.Add(_xEnumNamesAlias, enumsArray); + (schemaPropertyValue as OpenApiSchema)?.Extensions ??= new Dictionary(); + schemaPropertyValue.Extensions?.TryAdd(_xEnumNamesAlias, new JsonNodeExtension(enumsArray)); } if (_includeXEnumDescriptions) { - enumsDescriptionsArray.AddRange(EnumTypeExtensions + var enumsDescriptions = EnumTypeExtensions .GetEnumValuesDescription(genericArgumentType, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); - if (!schemaPropertyValue.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) + .Select(x => x.LastOrDefault().EnumDescription); + foreach (var enumDescription in enumsDescriptions) + { + enumsDescriptionsArray.Add(enumDescription); + } + + if (enumsDescriptionsArray.Any()) { - schemaPropertyValue.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); + (schemaPropertyValue as OpenApiSchema)?.Extensions ??= new Dictionary(); + schemaPropertyValue.Extensions?.TryAdd(_xEnumDescriptionsAlias, new JsonNodeExtension(enumsDescriptionsArray)); } } @@ -173,7 +197,7 @@ public void Apply( foreach (var schemaProperty in schema.Properties) { var schemaPropertyValue = schemaProperty.Value; - var propertySchema = context.SchemaRepository.Schemas.FirstOrDefault(s => schemaPropertyValue.AllOf.FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; + var propertySchema = context.SchemaRepository.Schemas.FirstOrDefault(s => schemaPropertyValue.AllOf?.OfType().FirstOrDefault(a => a.Reference.Id == s.Key) != null).Value; var description = propertySchema?.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeXEnumDescriptions, _newLine); if (description != null) { diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs index 7d56363..d4443b3 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs @@ -4,7 +4,7 @@ using System.Xml.XPath; using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -57,12 +57,12 @@ public void Apply( var controllerNamesAndTypes = context.ApiDescriptions .Select(apiDesc => apiDesc.ActionDescriptor as ControllerActionDescriptor) .SkipWhile(actionDesc => actionDesc == null) - .GroupBy(actionDesc => actionDesc.ControllerName) - .Select(group => new KeyValuePair(group.Key, group.First().ControllerTypeInfo.AsType())); + .GroupBy(actionDesc => actionDesc!.ControllerName) + .Select(group => new KeyValuePair(group.Key, group.First()?.ControllerTypeInfo.AsType())); foreach (var nameAndType in controllerNamesAndTypes) { - if (_excludedTypes.Any() && _excludedTypes.ToList().Contains(nameAndType.Value)) + if (_excludedTypes.Any() && nameAndType.Value != null && _excludedTypes.ToList().Contains(nameAndType.Value)) { continue; } @@ -73,13 +73,13 @@ public void Apply( var summaryNode = typeNode?.SelectSingleNode(SummaryTag); if (summaryNode != null) { - var remarksNode = typeNode.SelectSingleNode(RemarksTag); + var remarksNode = typeNode!.SelectSingleNode(RemarksTag); if (remarksNode != null && !string.IsNullOrWhiteSpace(remarksNode.InnerXml)) { - var tag = swaggerDoc.Tags.FirstOrDefault(t => t.Name.Equals(nameAndType.Key)); - if (tag != null && !tag.Description.Contains(XmlCommentsTextHelper.Humanize(remarksNode.InnerXml))) + var tag = swaggerDoc.Tags?.FirstOrDefault(t => t.Name?.Equals(nameAndType.Key) is true); + if (tag != null && tag.Description?.Contains(XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)) is not true) { - swaggerDoc.Tags.First(t => t.Name.Equals(nameAndType.Key)).Description += + swaggerDoc.Tags!.First(t => t.Name!.Equals(nameAndType.Key)).Description += $" ({XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)})"; } } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs index edd032c..c4bb0b5 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -48,7 +48,7 @@ public XmlCommentsWithRemarksParameterFilter( /// . /// . public void Apply( - OpenApiParameter parameter, + IOpenApiParameter parameter, ParameterFilterContext context) { if (context.PropertyInfo != null) @@ -57,7 +57,7 @@ public void Apply( } } - private void ApplyPropertyTags(OpenApiParameter parameter, PropertyInfo propertyInfo) + private void ApplyPropertyTags(IOpenApiParameter parameter, PropertyInfo propertyInfo) { if (propertyInfo == null) { @@ -75,10 +75,10 @@ private void ApplyPropertyTags(OpenApiParameter parameter, PropertyInfo property var summaryNode = propertyNode?.SelectSingleNode(SummaryTag); if (summaryNode != null) { - var remarksNode = propertyNode.SelectSingleNode(RemarksTag); + var remarksNode = propertyNode!.SelectSingleNode(RemarksTag); if (remarksNode != null && !string.IsNullOrWhiteSpace(remarksNode.InnerXml) - && !parameter.Description.Contains(XmlCommentsTextHelper.Humanize(remarksNode.InnerXml))) + && parameter.Description?.Contains(XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)) is not true) { parameter.Description += $" ({XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)})"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs index 51e1dc2..b33fc11 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -48,7 +48,7 @@ public XmlCommentsWithRemarksRequestBodyFilter( /// . /// . public void Apply( - OpenApiRequestBody requestBody, + IOpenApiRequestBody requestBody, RequestBodyFilterContext context) { var bodyParameterDescription = context.BodyParameterDescription; @@ -66,7 +66,7 @@ public void Apply( } } - private void ApplyPropertyTags(OpenApiRequestBody requestBody, PropertyInfo propertyInfo) + private void ApplyPropertyTags(IOpenApiRequestBody requestBody, PropertyInfo propertyInfo) { if (propertyInfo == null) { @@ -86,7 +86,7 @@ private void ApplyPropertyTags(OpenApiRequestBody requestBody, PropertyInfo prop var propertyRemarksNode = _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{propertyMemberName}']/{RemarksTag}"); if (propertyRemarksNode != null && !string.IsNullOrWhiteSpace(propertyRemarksNode.InnerXml) - && !requestBody.Description.Contains(XmlCommentsTextHelper.Humanize(propertyRemarksNode.InnerXml))) + && requestBody.Description?.Contains(XmlCommentsTextHelper.Humanize(propertyRemarksNode.InnerXml)) is not true) { requestBody.Description += $" ({XmlCommentsTextHelper.Humanize(propertyRemarksNode.InnerXml)})"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs index e382835..aa9f6c0 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -48,7 +48,7 @@ public XmlCommentsWithRemarksSchemaFilter( /// . /// . public void Apply( - OpenApiSchema schema, + IOpenApiSchema schema, SchemaFilterContext context) { ApplyTypeTags(schema, context.Type); @@ -63,7 +63,7 @@ public void Apply( } } - private void ApplyTypeTags(OpenApiSchema schema, Type type) + private void ApplyTypeTags(IOpenApiSchema schema, Type type) { if (_excludedTypes.Any() && _excludedTypes.ToList().Contains(type)) { @@ -83,7 +83,7 @@ private void ApplyTypeTags(OpenApiSchema schema, Type type) } } - private void ApplyFieldOrPropertyTags(OpenApiSchema schema, MemberInfo fieldOrPropertyInfo) + private void ApplyFieldOrPropertyTags(IOpenApiSchema schema, MemberInfo fieldOrPropertyInfo) { if (fieldOrPropertyInfo == null) { @@ -100,10 +100,10 @@ private void ApplyFieldOrPropertyTags(OpenApiSchema schema, MemberInfo fieldOrPr var summaryNode = fieldOrPropertyNode?.SelectSingleNode(SummaryTag); if (summaryNode != null) { - var remarksNode = fieldOrPropertyNode.SelectSingleNode(RemarksTag); + var remarksNode = fieldOrPropertyNode!.SelectSingleNode(RemarksTag); if (remarksNode != null && !string.IsNullOrWhiteSpace(remarksNode.InnerXml) - && !schema.Description.Contains(XmlCommentsTextHelper.Humanize(remarksNode.InnerXml))) + && schema.Description?.Contains(XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)) is not true) { schema.Description += $" ({XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)})"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Options/FixEnumsOptions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Options/FixEnumsOptions.cs index a3ce757..792e826 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Options/FixEnumsOptions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Options/FixEnumsOptions.cs @@ -11,6 +11,10 @@ namespace Unchase.Swashbuckle.AspNetCore.Extensions.Options /// public class FixEnumsOptions { + internal const string DefaultXEnumNamesAlias = "x-enumNames"; + + internal const string DefaultXEnumDescriptionsAlias = "x-enumDescriptions"; + #region Properties /// @@ -60,12 +64,12 @@ public class FixEnumsOptions /// /// Alias for replacing "x-enumNames" in swagger documentation. /// - public string XEnumNamesAlias { get; set; } = "x-enumNames"; + public string XEnumNamesAlias { get; set; } = DefaultXEnumNamesAlias; /// /// Alias for replacing "x-enumDescriptions" in swagger documentation. /// - public string XEnumDescriptionsAlias { get; set; } = "x-enumDescriptions"; + public string XEnumDescriptionsAlias { get; set; } = DefaultXEnumDescriptionsAlias; /// /// New line for enum values descriptions. diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj index 366e25b..b1a4591 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj @@ -1,7 +1,8 @@  - netstandard2.0 + net8.0 + enable Unchase Unchase Some additional useful extensions for Swashbuckle.AspNetCore. @@ -12,11 +13,11 @@ git Swagger;Swashbuckle;Filters;NSwag - 7.3 + 14.0 https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/blob/master/assets/icon.png?raw=true - 2.7.2 - 2.7.2.0 - 2.7.2.0 + 3.0.0 + 3.0.0.0 + 3.0.0.0 True Unchase.Swashbuckle.AspNetCore.Extensions.xml Unchase.Swashbuckle.AspNetCore.Extensions @@ -27,7 +28,7 @@ - + diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml index ec2e349..fb47ab4 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml @@ -26,39 +26,39 @@ - Extension methods for . + Extension methods for . - + Remove Paths and Components from OpenApi documentation for specific controller action without accepted roles. - . + . Action name selector. Collection of accepted roles. - Returns . + Returns . - + Remove Paths and Components from OpenApi documentation for specific controller action without accepted roles. - . + . Action name. Collection of accepted roles. - Returns . + Returns . - + Remove Paths and Components from OpenApi documentation for specific controller without accepted roles. - . + . Collection of accepted roles. - Returns . + Returns . @@ -262,11 +262,11 @@ Constructor. - + Apply filter. - + @@ -304,11 +304,11 @@ . New example for response. - + Apply filter. - . + . . @@ -318,11 +318,11 @@ . An to configure options for filter. - + Apply the filter. - . + . . @@ -336,20 +336,20 @@ - + Remove Paths and Components from OpenApi documentation without accepted roles. - . + . Dictionary of openApi paths with keys. Dictionary with openApi schemas with scheme name keys. Collection of accepted roles. - + Apply filter. - . + . . @@ -369,11 +369,11 @@ Excluded types. List of . - + Apply filter. - . + . . @@ -399,11 +399,11 @@ List of . Excluded types. - + Apply filter. - . + . . @@ -452,11 +452,11 @@ Excluded types. List of . - + Apply filter. - . + . . @@ -464,11 +464,11 @@ Document filter for ordering tags by name in OpenApi document. - + Apply filter. - . + . . @@ -478,11 +478,11 @@ . An to configure options for filter. - + Apply the filter. - . + . . @@ -492,11 +492,11 @@ . An to configure options for filter. - + Apply the filter. - . + . . @@ -511,11 +511,11 @@ Excluded types. - + Apply filter. - . + . . @@ -530,11 +530,11 @@ Excluded types. - + Apply filter. - . + . . @@ -549,11 +549,11 @@ Excluded types. - + Apply filter. - . + . . @@ -568,11 +568,11 @@ Excluded types. - + Apply filter. - . + . . diff --git a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj index 24534a3..ee75198 100644 --- a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj +++ b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net8.0 false @@ -17,8 +17,8 @@ - - + + diff --git a/test/WebApi3.1-Swashbuckle/Program.cs b/test/WebApi3.1-Swashbuckle/Program.cs deleted file mode 100644 index ca876e4..0000000 --- a/test/WebApi3.1-Swashbuckle/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; - -namespace WebApi3._1_Swashbuckle -{ -#pragma warning disable CS1591 - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -#pragma warning restore CS1591 -} diff --git a/test/WebApi3.1-Swashbuckle/Properties/launchSettings.json b/test/WebApi3.1-Swashbuckle/Properties/launchSettings.json deleted file mode 100644 index e998a66..0000000 --- a/test/WebApi3.1-Swashbuckle/Properties/launchSettings.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:27183", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "WebApi3._1_Swashbuckle": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/test/WebApi3.1-Swashbuckle/Startup.cs b/test/WebApi3.1-Swashbuckle/Startup.cs deleted file mode 100644 index 932a19b..0000000 --- a/test/WebApi3.1-Swashbuckle/Startup.cs +++ /dev/null @@ -1,202 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.OpenApi.Models; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; -using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; -using Unchase.Swashbuckle.AspNetCore.Extensions.Options; -using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Controllers; -using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models; - -namespace WebApi3._1_Swashbuckle -{ -#pragma warning disable CS1591 - public class Startup - { - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers(); - //.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); - - // Register the Swagger generator - services.AddSwaggerGen(options => - { - options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); - - // use it if you want to hide Paths and Definitions from OpenApi documentation correctly - options.UseAllOfToExtendReferenceSchemas(); - - // if you want to add xml comments from summary and remarks into the swagger documentation, first of all add: - // you can exclude remarks for concrete types - var xmlFilePath = Path.Combine(AppContext.BaseDirectory, "Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml"); - options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, - typeof(ComplicatedClass), - typeof(InnerEnum)); - - xmlFilePath = Path.Combine(AppContext.BaseDirectory, "WebApi3.1-Swashbuckle.xml"); - options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, - typeof(ComplicatedClass), - typeof(InnerEnum)); - - // if you want to add xml comments from inheritdocs (from summary and remarks) into the swagger documentation, add: - // you can exclude remarks for concrete types - options.IncludeXmlCommentsFromInheritDocs(includeRemarks: true); - - // options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, () => new[] { typeof(InnerEnum) }); - - // or add without remarks - //options.IncludeXmlComments(xmlFilePath); - - #region AddEnumsWithValuesFixFilters - - // Add filters to fix enums - // use by default: - options.AddEnumsWithValuesFixFilters(); - - // or configured: - options.AddEnumsWithValuesFixFilters(o => - { - // add schema filter to fix enums (add 'x-enumNames' for NSwag or its alias from XEnumNamesAlias) in schema - o.ApplySchemaFilter = true; - - // alias for replacing 'x-enumNames' in swagger document - o.XEnumNamesAlias = "x-enum-varnames"; - - // alias for replacing 'x-enumDescriptions' in swagger document - o.XEnumDescriptionsAlias = "x-enum-descriptions"; - - // new line for enum values descriptions - // o.NewLine = Environment.NewLine; - o.NewLine = "\n"; - - // add parameter filter to fix enums (add 'x-enumNames' for NSwag or its alias from XEnumNamesAlias) in schema parameters - o.ApplyParameterFilter = true; - - // add document filter to fix enums displaying in swagger document - o.ApplyDocumentFilter = true; - - // add descriptions from DescriptionAttribute or xml-comments to fix enums (add 'x-enumDescriptions' or its alias from XEnumDescriptionsAlias for schema extensions) for applied filters - o.IncludeDescriptions = true; - - // add remarks for descriptions from xml-comments - o.IncludeXEnumRemarks = true; - - // get descriptions from DescriptionAttribute then from xml-comments - o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments; - - // new line for enum values descriptions - // o.NewLine = Environment.NewLine; - o.NewLine = "\n"; - - // get descriptions from xml-file comments on the specified path - // should use "options.IncludeXmlComments(xmlFilePath);" before - o.IncludeXmlCommentsFrom(xmlFilePath); - // the same for another xml-files... - }); - - #endregion - - #region HidePathsAndDefinitionsByRolesDocumentFilter - - // remove Paths and Components from OpenApi documentation without accepted roles - options.DocumentFilter(new List { "AcceptedRole" }); - - #endregion - - #region AppendActionCountToTagSummaryDocumentFilter - - // enable openApi Annotations - options.EnableAnnotations(); - - // add action count (with message template) into the SwaggerTag's descriptions - // you can use it after "HidePathsAndDefinitionsByRolesDocumentFilter" - options.DocumentFilter("(the count of actions: {0})"); - - #endregion - - #region TagOrderByNameDocumentFilter - - // order tags by name - options.DocumentFilter(); - - #endregion - - #region ChangeAllResponsesByHttpStatusCode - - // change responses for specific HTTP status code ("200") - //options.ChangeAllResponsesByHttpStatusCode( - // httpStatusCode: 200, - // responseDescription: "200 status code description", - // responseExampleOption: ResponseExampleOptions.AddNew, // add new response examples - // responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples - - // change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: HttpStatusCode.BadRequest, - responseDescription: "400 status code description", - responseExampleOption: ResponseExampleOptions.Clear, // claer response examples - responseExample: new ComplicatedClass()); // some class for response examples - - // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: StatusCodes.Status201Created, - responseDescription: "201 status code description", - responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples - responseExample: new ComplicatedClass()); // some class for response examples - - #endregion - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - // enable middleware to serve generated Swagger as a JSON endpoint. - app.UseSwagger(c => - { - c.PreSerializeFilters.Add((openApiDoc, httpRequest) => - { - // remove Paths and Components from OpenApi documentation for specific controller action without accepted roles - openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(controller => nameof(controller.HidedAction), new List {"AcceptedRole"}); - - // or - //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(nameof(HidedController.HidedAction), new List { "AcceptedRole" }); - - - // remove Paths and Components from OpenApi documentation for all controller actions without accepted roles - openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(new List {"AcceptedRole"}); - - // or you can get accepted roles by httpRequest like this: - //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(GetAcceptedRolesByRemoteIp(httpRequest.HttpContext.Connection.RemoteIpAddress)); - }); - }); - - // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), - // specifying the Swagger JSON endpoint. - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - }); - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -#pragma warning restore CS1591 -} diff --git a/test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.csproj b/test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.csproj deleted file mode 100644 index b698b2d..0000000 --- a/test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp3.1 - WebApi3._1_Swashbuckle - - - - WebApi3.1-Swashbuckle.xml - - - - WebApi3.1-Swashbuckle.xml - - - - - - - diff --git a/test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.xml b/test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.xml deleted file mode 100644 index 9d375fc..0000000 --- a/test/WebApi3.1-Swashbuckle/WebApi3.1-Swashbuckle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - WebApi3.1-Swashbuckle - - - - diff --git a/test/WebApi3.1-Swashbuckle/appsettings.Development.json b/test/WebApi3.1-Swashbuckle/appsettings.Development.json deleted file mode 100644 index 8983e0f..0000000 --- a/test/WebApi3.1-Swashbuckle/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/test/WebApi3.1-Swashbuckle/appsettings.json b/test/WebApi3.1-Swashbuckle/appsettings.json deleted file mode 100644 index d9d9a9b..0000000 --- a/test/WebApi3.1-Swashbuckle/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/test/WebApi5-Swashbuckle/Controllers/RecordsController.cs b/test/WebApi5-Swashbuckle/Controllers/RecordsController.cs deleted file mode 100644 index 9980290..0000000 --- a/test/WebApi5-Swashbuckle/Controllers/RecordsController.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Swashbuckle.AspNetCore.Annotations; -using WebApi5_Swashbuckle.Models; - -namespace WebApi5_Swashbuckle.Controllers -{ - /// - /// Records controller - /// - [Produces("application/json")] - [Route("api/[controller]")] - [ApiController] - [SwaggerTag("Records controller")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status201Created)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(420)] - public class RecordsController : ControllerBase - { - /// - /// Get record response action - /// - /// - /// Get record response action remarks - /// - [HttpGet("get-record-response")] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRecordResponseAction() - { - return Ok(new RecordResponse(5, "name1")); - } - } -} diff --git a/test/WebApi5-Swashbuckle/Models/RecordResponse.cs b/test/WebApi5-Swashbuckle/Models/RecordResponse.cs deleted file mode 100644 index 50434e4..0000000 --- a/test/WebApi5-Swashbuckle/Models/RecordResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace WebApi5_Swashbuckle.Models -{ - /// - /// Record response. - /// - /// - /// Just record response. - /// - /// The code. - /// The name. - public record RecordResponse(int Code, string Name); -} diff --git a/test/WebApi5-Swashbuckle/Program.cs b/test/WebApi5-Swashbuckle/Program.cs deleted file mode 100644 index a224011..0000000 --- a/test/WebApi5-Swashbuckle/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; - -namespace WebApi5_Swashbuckle -{ -#pragma warning disable CS1591 - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -#pragma warning restore CS1591 -} diff --git a/test/WebApi5-Swashbuckle/Properties/launchSettings.json b/test/WebApi5-Swashbuckle/Properties/launchSettings.json deleted file mode 100644 index 6aac5b3..0000000 --- a/test/WebApi5-Swashbuckle/Properties/launchSettings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:2482", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "WebApi5_Swashbuckle": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/test/WebApi5-Swashbuckle/Startup.cs b/test/WebApi5-Swashbuckle/Startup.cs deleted file mode 100644 index 4d61226..0000000 --- a/test/WebApi5-Swashbuckle/Startup.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using Microsoft.AspNetCore.Http; -using Microsoft.OpenApi.Models; -using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; -using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; -using Unchase.Swashbuckle.AspNetCore.Extensions.Options; -using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Controllers; -using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models; - -namespace WebApi5_Swashbuckle -{ -#pragma warning disable CS1591 - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - - services.AddControllers(); - - // Register the Swagger generator - services.AddSwaggerGen(options => - { - options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); - - // use it if you want to hide Paths and Definitions from OpenApi documentation correctly - options.UseAllOfToExtendReferenceSchemas(); - - // if you want to add xml comments from summary and remarks into the swagger documentation, first of all add: - // you can exclude remarks for concrete types - var xmlFilePath = Path.Combine(AppContext.BaseDirectory, "Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml"); - options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, - typeof(ComplicatedClass), - typeof(InnerEnum)); - - xmlFilePath = Path.Combine(AppContext.BaseDirectory, "WebApi5-Swashbuckle.xml"); - options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, - typeof(ComplicatedClass), - typeof(InnerEnum)); - - // if you want to add xml comments from inheritdocs (from summary and remarks) into the swagger documentation, add: - // you can exclude remarks for concrete types - options.IncludeXmlCommentsFromInheritDocs(includeRemarks: true); - - // options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, () => new[] { typeof(InnerEnum) }); - - // or add without remarks - //options.IncludeXmlComments(xmlFilePath); - - #region AddEnumsWithValuesFixFilters - - // Add filters to fix enums - // use by default: - options.AddEnumsWithValuesFixFilters(); - - // or configured: - options.AddEnumsWithValuesFixFilters(o => - { - // add schema filter to fix enums (add 'x-enumNames' for NSwag or its alias from XEnumNamesAlias) in schema - o.ApplySchemaFilter = true; - - // alias for replacing 'x-enumNames' in swagger document - o.XEnumNamesAlias = "x-enum-varnames"; - - // alias for replacing 'x-enumDescriptions' in swagger document - o.XEnumDescriptionsAlias = "x-enum-descriptions"; - - // add parameter filter to fix enums (add 'x-enumNames' for NSwag or its alias from XEnumNamesAlias) in schema parameters - o.ApplyParameterFilter = true; - - // add document filter to fix enums displaying in swagger document - o.ApplyDocumentFilter = true; - - // add descriptions from DescriptionAttribute or xml-comments to fix enums (add 'x-enumDescriptions' for schema extensions) for applied filters - o.IncludeDescriptions = true; - - // add remarks for descriptions from xml-comments - o.IncludeXEnumRemarks = true; - - // get descriptions from DescriptionAttribute then from xml-comments - o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments; - - // new line for enum values descriptions - // o.NewLine = Environment.NewLine; - o.NewLine = "\n"; - - // get descriptions from xml-file comments on the specified path - // should use "options.IncludeXmlComments(xmlFilePath);" before - o.IncludeXmlCommentsFrom(xmlFilePath); - // the same for another xml-files... - }); - - #endregion - - #region HidePathsAndDefinitionsByRolesDocumentFilter - - // remove Paths and Components from OpenApi documentation without accepted roles - options.DocumentFilter(new List { "AcceptedRole" }); - - #endregion - - #region AppendActionCountToTagSummaryDocumentFilter - - // enable openApi Annotations - options.EnableAnnotations(); - - // add action count (with message template) into the SwaggerTag's descriptions - // you can use it after "HidePathsAndDefinitionsByRolesDocumentFilter" - options.DocumentFilter("(the count of actions: {0})"); - - #endregion - - #region TagOrderByNameDocumentFilter - - // order tags by name - options.DocumentFilter(); - - #endregion - - #region ChangeAllResponsesByHttpStatusCode - - // change responses for specific HTTP status code ("200") - //options.ChangeAllResponsesByHttpStatusCode( - // httpStatusCode: 200, - // responseDescription: "200 status code description", - // responseExampleOption: ResponseExampleOptions.AddNew, // add new response examples - // responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples - - // change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: HttpStatusCode.BadRequest, - responseDescription: "400 status code description", - responseExampleOption: ResponseExampleOptions.Clear, // claer response examples - responseExample: new ComplicatedClass()); // some class for response examples - - // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: StatusCodes.Status201Created, - responseDescription: "201 status code description", - responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples - responseExample: new ComplicatedClass()); // some class for response examples - - #endregion - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - // enable middleware to serve generated Swagger as a JSON endpoint. - app.UseSwagger(c => - { - c.PreSerializeFilters.Add((openApiDoc, httpRequest) => - { - // remove Paths and Components from OpenApi documentation for specific controller action without accepted roles - openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(controller => nameof(controller.HidedAction), new List { "AcceptedRole" }); - - // or - //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(nameof(HidedController.HidedAction), new List { "AcceptedRole" }); - - - // remove Paths and Components from OpenApi documentation for all controller actions without accepted roles - openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(new List { "AcceptedRole" }); - - // or you can get accepted roles by httpRequest like this: - //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(GetAcceptedRolesByRemoteIp(httpRequest.HttpContext.Connection.RemoteIpAddress)); - }); - }); - - // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), - // specifying the Swagger JSON endpoint. - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - }); - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -#pragma warning restore CS1591 -} diff --git a/test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.csproj b/test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.csproj deleted file mode 100644 index 9ed1476..0000000 --- a/test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - net5.0 - WebApi5_Swashbuckle - - - - WebApi5-Swashbuckle.xml - - - - WebApi5-Swashbuckle.xml - - - - - - - diff --git a/test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.xml b/test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.xml deleted file mode 100644 index 576d5a7..0000000 --- a/test/WebApi5-Swashbuckle/WebApi5-Swashbuckle.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - WebApi5-Swashbuckle - - - - - Records controller - - - - - Get record response action - - - Get record response action remarks - - - - - Record response. - - - Just record response. - - The code. - The name. - - - - Record response. - - - Just record response. - - The code. - The name. - - - The code. - - - The name. - - - diff --git a/test/WebApi5-Swashbuckle/appsettings.Development.json b/test/WebApi5-Swashbuckle/appsettings.Development.json deleted file mode 100644 index 8983e0f..0000000 --- a/test/WebApi5-Swashbuckle/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/test/WebApi5-Swashbuckle/appsettings.json b/test/WebApi5-Swashbuckle/appsettings.json deleted file mode 100644 index d9d9a9b..0000000 --- a/test/WebApi5-Swashbuckle/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/test/WebApi7-Swashbuckle/Controllers/RecordsController.cs b/test/WebApi8-Swashbuckle/Controllers/RecordsController.cs similarity index 100% rename from test/WebApi7-Swashbuckle/Controllers/RecordsController.cs rename to test/WebApi8-Swashbuckle/Controllers/RecordsController.cs diff --git a/test/WebApi7-Swashbuckle/Models/RecordResponse.cs b/test/WebApi8-Swashbuckle/Models/RecordResponse.cs similarity index 100% rename from test/WebApi7-Swashbuckle/Models/RecordResponse.cs rename to test/WebApi8-Swashbuckle/Models/RecordResponse.cs diff --git a/test/WebApi7-Swashbuckle/Program.cs b/test/WebApi8-Swashbuckle/Program.cs similarity index 98% rename from test/WebApi7-Swashbuckle/Program.cs rename to test/WebApi8-Swashbuckle/Program.cs index 163d659..a4bf655 100644 --- a/test/WebApi7-Swashbuckle/Program.cs +++ b/test/WebApi8-Swashbuckle/Program.cs @@ -1,6 +1,6 @@ using System.Net; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -28,7 +28,7 @@ typeof(InnerEnum)); var xmlFilePath1 = xmlFilePath; - xmlFilePath = Path.Combine(AppContext.BaseDirectory, "WebApi7-Swashbuckle.xml"); + xmlFilePath = Path.Combine(AppContext.BaseDirectory, "WebApi8-Swashbuckle.xml"); options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, typeof(ComplicatedClass), typeof(InnerEnum)); @@ -174,8 +174,6 @@ c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); -app.UseRouting(); - -app.UseEndpoints(endpoints => endpoints.MapControllers()); +app.MapControllers(); app.Run(); \ No newline at end of file diff --git a/test/WebApi7-Swashbuckle/Properties/launchSettings.json b/test/WebApi8-Swashbuckle/Properties/launchSettings.json similarity index 96% rename from test/WebApi7-Swashbuckle/Properties/launchSettings.json rename to test/WebApi8-Swashbuckle/Properties/launchSettings.json index d16400f..6b9c4b4 100644 --- a/test/WebApi7-Swashbuckle/Properties/launchSettings.json +++ b/test/WebApi8-Swashbuckle/Properties/launchSettings.json @@ -8,7 +8,7 @@ } }, "profiles": { - "WebApi7_Swashbuckle": { + "WebApi8_Swashbuckle": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, diff --git a/test/WebApi7-Swashbuckle/WebApi7-Swashbuckle.csproj b/test/WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj similarity index 79% rename from test/WebApi7-Swashbuckle/WebApi7-Swashbuckle.csproj rename to test/WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj index 6769e7f..72334ce 100644 --- a/test/WebApi7-Swashbuckle/WebApi7-Swashbuckle.csproj +++ b/test/WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj @@ -1,10 +1,10 @@ - net7.0 + net8.0 enable enable - WebApi7_Swashbuckle + WebApi8_Swashbuckle True diff --git a/test/WebApi7-Swashbuckle/appsettings.Development.json b/test/WebApi8-Swashbuckle/appsettings.Development.json similarity index 100% rename from test/WebApi7-Swashbuckle/appsettings.Development.json rename to test/WebApi8-Swashbuckle/appsettings.Development.json diff --git a/test/WebApi7-Swashbuckle/appsettings.json b/test/WebApi8-Swashbuckle/appsettings.json similarity index 100% rename from test/WebApi7-Swashbuckle/appsettings.json rename to test/WebApi8-Swashbuckle/appsettings.json From 1553eec8b39c3779c211fa984362c7b520247725 Mon Sep 17 00:00:00 2001 From: Ao Rui Date: Mon, 26 Jan 2026 15:58:50 +0800 Subject: [PATCH 2/3] fix: fix nullable reference types warnings. --- .../Extensions/OpenApiDocumentExtensions.cs | 12 +++++++----- .../Extensions/XmlCommentsExtensions.cs | 17 +++++++++-------- .../Factories/ApiDescriptionFactory.cs | 15 ++++++++++----- ...pendActionCountToTagSummaryDocumentFilter.cs | 12 +++++++----- .../DisplayEnumsWithValuesDocumentFilter.cs | 16 ++++++++++------ ...ePathsAndDefinitionsByRolesDocumentFilter.cs | 2 ++ .../Filters/InheritDocOperationFilter.cs | 10 +++++----- .../Filters/InheritDocParameterFilter.cs | 7 ++++--- .../Filters/InheritDocRequestBodyFilter.cs | 6 +++--- .../Filters/InheritDocSchemaFilter.cs | 4 ++-- .../Filters/TagOrderByNameDocumentFilter.cs | 2 +- .../Filters/XEnumNamesParameterFilter.cs | 8 +++++--- 12 files changed, 65 insertions(+), 46 deletions(-) diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs index 71fb2e2..5978d0e 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs @@ -33,7 +33,7 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesFor()?.Template)?.ActionDescriptor; - if (actionDescriptor != null) + if (actionDescriptor?.AttributeRouteInfo?.Template != null && openApiDoc.Components?.Schemas != null) { var paths = new Dictionary<(MethodInfo, Type), string> { @@ -60,8 +60,8 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesFor acceptedRoles) where TController : class { - var actionDescriptor = ApiDescriptionFactory.Create(typeof(TController), actionName, typeof(TController).GetCustomAttribute().Template)?.ActionDescriptor; - if (actionDescriptor != null) + var actionDescriptor = ApiDescriptionFactory.Create(typeof(TController), actionName, typeof(TController).GetCustomAttribute()?.Template)?.ActionDescriptor; + if (actionDescriptor?.AttributeRouteInfo?.Template != null && openApiDoc.Components?.Schemas != null) { var paths = new Dictionary<(MethodInfo, Type), string> { @@ -86,11 +86,13 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesForCon this OpenApiDocument openApiDoc, IReadOnlyList acceptedRoles) where TController : class { + if (openApiDoc.Components?.Schemas == null) return openApiDoc; + var paths = new Dictionary<(MethodInfo, Type), string>(); foreach (var methodInfo in typeof(TController).GetMethods().Where(m => !m.IsSpecialName)) { - var actionDescriptor = ApiDescriptionFactory.Create(methodInfo, typeof(TController).GetCustomAttribute().Template)?.ActionDescriptor; - if (actionDescriptor != null) + var actionDescriptor = ApiDescriptionFactory.Create(methodInfo, typeof(TController).GetCustomAttribute()?.Template)?.ActionDescriptor; + if (actionDescriptor?.AttributeRouteInfo?.Template != null) { paths.Add((((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)actionDescriptor).MethodInfo, typeof(TController)), actionDescriptor.AttributeRouteInfo.Template); } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs index a202ea9..71d323e 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs @@ -15,7 +15,7 @@ internal static class XmlCommentsExtensions private const string RemarksTag = "remarks"; private const string ExampleTag = "example"; - internal static Type GetTargetRecursive(this Type type, Dictionary inheritedDocs, string cref) + internal static Type? GetTargetRecursive(this Type type, Dictionary inheritedDocs, string cref) { var targets = GetTargets(type, cref); @@ -51,7 +51,7 @@ internal static Type GetTargetRecursive(this Type type, Dictionary inheritedDocs, string cref) + internal static MemberInfo? GetTargetRecursive(this MemberInfo memberInfo, Dictionary inheritedDocs, string cref) { var targets = GetTargets(memberInfo, cref); - if (!targets.Any()) + if (targets?.Any() != true) { return null; } @@ -103,7 +103,7 @@ internal static MemberInfo GetTargetRecursive(this MemberInfo memberInfo, Dictio return null; } - private static MemberInfo[] GetTargets(MemberInfo memberInfo, string cref) + private static MemberInfo[]? GetTargets(MemberInfo memberInfo, string cref) { var type = memberInfo.DeclaringType ?? memberInfo.ReflectedType; @@ -115,8 +115,9 @@ private static MemberInfo[] GetTargets(MemberInfo memberInfo, string cref) // Find all matching members in all interfaces and the base class. var targets = type.GetInterfaces() .Append(type.BaseType) + .Where(x => x != null) .SelectMany( - x => x.FindMembers( + x => x!.FindMembers( memberInfo.MemberType, BindingFlags.Instance | BindingFlags.Public, (info, _) => info.Name == memberInfo.Name, @@ -164,7 +165,7 @@ internal static void ApplyPropertyComments( } string cref = inheritedDocs[memberName].Cref; - XPathNavigator targetXmlNode; + XPathNavigator? targetXmlNode; if (string.IsNullOrWhiteSpace(cref)) { var target = memberInfo.GetTargetRecursive(inheritedDocs, cref); @@ -210,7 +211,7 @@ internal static void ApplyPropertyComments( } } - internal static XPathNavigator GetMemberXmlNode(string memberName, List documents) + internal static XPathNavigator? GetMemberXmlNode(string memberName, List documents) { string path = $"/doc/members/member[@name='{memberName}']"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs index 065f3ce..43d2cec 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs @@ -239,8 +239,10 @@ internal static class ApiDescriptionFactory ); } - private static ActionDescriptor CreateActionDescriptor(MethodInfo methodInfo) + private static ActionDescriptor? CreateActionDescriptor(MethodInfo methodInfo) { + if (methodInfo.DeclaringType == null) return null; + var httpMethodAttribute = methodInfo.GetCustomAttribute(); var attributeRouteInfo = (httpMethodAttribute != null) ? new AttributeRouteInfo { Template = httpMethodAttribute.Template, Name = httpMethodAttribute.Name } @@ -248,11 +250,12 @@ private static ActionDescriptor CreateActionDescriptor(MethodInfo methodInfo) var parameterDescriptors = methodInfo.GetParameters() .Select(CreateParameterDescriptor) + .Where(x => x != null) .ToList(); - var routeValues = new Dictionary + var routeValues = new Dictionary { - ["controller"] = methodInfo.DeclaringType?.Name.Replace("Controller", string.Empty) + ["controller"] = methodInfo.DeclaringType.Name.Replace("Controller", string.Empty) }; return new ControllerActionDescriptor @@ -260,13 +263,15 @@ private static ActionDescriptor CreateActionDescriptor(MethodInfo methodInfo) AttributeRouteInfo = attributeRouteInfo, ControllerTypeInfo = methodInfo.DeclaringType.GetTypeInfo(), MethodInfo = methodInfo, - Parameters = parameterDescriptors, + Parameters = parameterDescriptors!, RouteValues = routeValues }; } - private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo parameterInfo) + private static ParameterDescriptor? CreateParameterDescriptor(ParameterInfo parameterInfo) { + if (parameterInfo.Name == null) return null; + return new ControllerParameterDescriptor { Name = parameterInfo.Name, diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs index ee159aa..05d9437 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs @@ -47,18 +47,20 @@ public void Apply(OpenApiDocument openApiDoc, DocumentFilterContext context) var tagActionCount = new Dictionary(); foreach (var path in openApiDoc.Paths) { - var possibleParameterizedOperations = path.Value.Operations.Select(o => o.Value); - possibleParameterizedOperations.Where(o => o?.Tags != null).ToList().ForEach(o => + var possibleParameterizedOperations = path.Value.Operations?.Select(o => o.Value); + possibleParameterizedOperations?.Where(o => o?.Tags != null).ToList().ForEach(o => { - foreach (var tag in o.Tags) + foreach (var tag in o.Tags!) { - if (!tagActionCount.ContainsKey(tag.Name)) + if (tag.Name == null) continue; + + if (!tagActionCount.TryGetValue(tag.Name, out int value)) { tagActionCount.Add(tag.Name, 1); } else { - tagActionCount[tag.Name]++; + tagActionCount[tag.Name] = ++value; } } }); diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs index 3a3f0b1..a7c17e6 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs @@ -66,7 +66,7 @@ public void Apply( return; } - foreach (var schemaDictionaryItem in openApiDoc.Components.Schemas) + foreach (var schemaDictionaryItem in openApiDoc.Components?.Schemas?.AsEnumerable() ?? []) { var schema = schemaDictionaryItem.Value; var description = schema.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); @@ -89,9 +89,13 @@ public void Apply( } // add enum descriptions to input parameters of every operation - foreach (var parameter in openApiDoc.Paths.Values.SelectMany(v => v.Operations).SelectMany(op => op.Value.Parameters)) + foreach (var parameter in openApiDoc.Paths.Values + .Where(v => v.Operations != null) + .SelectMany(v => v.Operations!) + .Where(op => op.Value.Parameters != null) + .SelectMany(op => op.Value.Parameters!)) { - IOpenApiSchema schema = null; + IOpenApiSchema? schema = null; if ((parameter.Schema as OpenApiSchemaReference)?.Reference == null) { if (parameter.Schema?.AllOf?.Count > 0) @@ -105,10 +109,10 @@ public void Apply( } else { - var componentReference = (parameter.Schema as OpenApiSchemaReference).Reference.Id; + var componentReference = (parameter.Schema as OpenApiSchemaReference)?.Reference.Id; if (!string.IsNullOrWhiteSpace(componentReference)) { - schema = openApiDoc.Components.Schemas[componentReference]; + schema = openApiDoc.Components?.Schemas?[componentReference]; } } @@ -130,7 +134,7 @@ public void Apply( } // add enum descriptions to request body - foreach (var operation in openApiDoc.Paths.Values.SelectMany(v => v.Operations)) + foreach (var operation in openApiDoc.Paths.Values.Where(v => v.Operations != null).SelectMany(v => v.Operations!)) { var requestBodyContents = operation.Value.RequestBody?.Content; if (requestBodyContents != null) diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs index 432af38..6de59bd 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs @@ -366,6 +366,8 @@ public void Apply( var paths = new Dictionary<(MethodInfo, Type), string>(); foreach (var actionDescriptor in apiDescriptions.Select(ad => ad.ActionDescriptor)) { + if (actionDescriptor.AttributeRouteInfo?.Template == null) continue; + var t = ((ControllerActionDescriptor)actionDescriptor).ControllerTypeInfo.AsType(); paths.Add((((ControllerActionDescriptor)actionDescriptor).MethodInfo, t), actionDescriptor.AttributeRouteInfo.Template); } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs index 9badc5c..8e8bb66 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs @@ -78,7 +78,7 @@ public void Apply( if (_inheritedDocs.ContainsKey(memberName)) { string cref = _inheritedDocs[memberName].Cref; - XPathNavigator targetXmlNode; + XPathNavigator? targetXmlNode; var originXmlNode = XmlCommentsExtensions.GetMemberXmlNode(memberName, _documents); if (string.IsNullOrWhiteSpace(cref)) { @@ -157,7 +157,7 @@ public void Apply( } var paramNode = targetXmlNode?.SelectSingleNode(string.Format(ParamXPath, parameterInfo.Name)); - var parameter = operation.Parameters.FirstOrDefault(x => x.Name.Equals(parameterInfo.Name)); + var parameter = operation.Parameters?.FirstOrDefault(x => x.Name?.Equals(parameterInfo.Name) == true); if (parameter != null) { if (paramNode != null) @@ -174,7 +174,7 @@ public void Apply( continue; } - IOpenApiSchema schema = null; + IOpenApiSchema? schema = null; if ((parameter.Schema as OpenApiSchemaReference)?.Reference == null) { if (parameter.Schema?.AllOf?.Count > 0) @@ -185,9 +185,9 @@ public void Apply( { if (parameter.Description == null) { - parameter.Description = XmlCommentsTextHelper.Humanize(paramNode.InnerXml); + if (paramNode != null) parameter.Description = XmlCommentsTextHelper.Humanize(paramNode.InnerXml); } - else if (!parameter.Description.Contains(paramNode.InnerXml)) + else if (paramNode != null && !parameter.Description.Contains(paramNode.InnerXml)) { parameter.Description += XmlCommentsTextHelper.Humanize(paramNode.InnerXml); } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs index 8927f07..102ea45 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs @@ -82,7 +82,8 @@ public void Apply( return; } - if (_excludedTypes.Any() && _excludedTypes.ToList().Contains(context.ApiParameterDescription.PropertyInfo().DeclaringType)) + var propertyDeclaringType = context.ApiParameterDescription.PropertyInfo().DeclaringType; + if (propertyDeclaringType != null && _excludedTypes.Any() && _excludedTypes.ToList().Contains(propertyDeclaringType)) { return; } @@ -97,7 +98,7 @@ public void Apply( if (string.IsNullOrWhiteSpace(parameter.Description) && _inheritedDocs.ContainsKey(parameterMemberName)) { string cref = _inheritedDocs[parameterMemberName].Cref; - XPathNavigator targetXmlNode; + XPathNavigator? targetXmlNode; if (string.IsNullOrWhiteSpace(cref)) { var target = context.ApiParameterDescription.PropertyInfo().GetTargetRecursive(_inheritedDocs, cref); @@ -120,7 +121,7 @@ public void Apply( if (_includeRemarks) { - var remarksNode = targetXmlNode.SelectSingleNode(RemarksTag); + var remarksNode = targetXmlNode?.SelectSingleNode(RemarksTag); if (remarksNode != null && !string.IsNullOrWhiteSpace(remarksNode.InnerXml)) { parameter.Description += $" ({XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)})"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs index 945139d..f38a90e 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs @@ -85,7 +85,7 @@ public void Apply( private void ApplyForType( IOpenApiRequestBody requestBody, RequestBodyFilterContext context, - Type type) + Type? type) { if (type == null) { @@ -102,7 +102,7 @@ private void ApplyForType( if (string.IsNullOrWhiteSpace(requestBody.Description) && _inheritedDocs.ContainsKey(parameterMemberName)) { string cref = _inheritedDocs[parameterMemberName].Cref; - XPathNavigator targetXmlNode; + XPathNavigator? targetXmlNode; if (string.IsNullOrWhiteSpace(cref)) { var target = type.GetTargetRecursive(_inheritedDocs, cref); @@ -125,7 +125,7 @@ private void ApplyForType( if (_includeRemarks) { - var remarksNode = targetXmlNode.SelectSingleNode(RemarksTag); + var remarksNode = targetXmlNode?.SelectSingleNode(RemarksTag); if (remarksNode != null && !string.IsNullOrWhiteSpace(remarksNode.InnerXml)) { requestBody.Description += $" ({XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)})"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs index daddce3..d9e2ad5 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs @@ -86,7 +86,7 @@ public void Apply( if (string.IsNullOrWhiteSpace(schema.Description) && _inheritedDocs.ContainsKey(memberName)) { string cref = _inheritedDocs[memberName].Cref; - XPathNavigator targetXmlNode; + XPathNavigator? targetXmlNode; if (string.IsNullOrWhiteSpace(cref)) { var target = context.Type.GetTargetRecursive(_inheritedDocs, cref); @@ -109,7 +109,7 @@ public void Apply( if (_includeRemarks) { - var remarksNode = targetXmlNode.SelectSingleNode(RemarksTag); + var remarksNode = targetXmlNode?.SelectSingleNode(RemarksTag); if (remarksNode != null && !string.IsNullOrWhiteSpace(remarksNode.InnerXml)) { schema.Description += $" ({XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)})"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs index 39a1f85..951ad7e 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs @@ -22,7 +22,7 @@ public void Apply( OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Tags = swaggerDoc.Tags.OrderBy(t => t.Name).ToHashSet(); + swaggerDoc.Tags = swaggerDoc.Tags?.OrderBy(t => t.Name).ToHashSet(); } #endregion diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs index f29b2dc..855e5d5 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs @@ -176,7 +176,7 @@ public void Apply( else if (genericArgumentType.IsArray && genericArgumentType.GetElementType()?.IsEnum == true) { var parameterEnumSchemaReference = parameter.Schema?.Items?.Items as OpenApiSchemaReference; - if (parameterEnumSchemaReference != null) + if (parameterEnumSchemaReference?.Reference.Id != null) { var enumSchema = context.SchemaRepository.Schemas[parameterEnumSchemaReference.Reference.Id]; var description = enumSchema.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); @@ -198,8 +198,10 @@ public void Apply( else if (typeInfo.IsArray && typeInfo.GetElementType()?.IsEnum == true) { var parameterEnumSchemaReference = parameter.Schema?.Items as OpenApiSchemaReference; - var enumSchema = context.SchemaRepository.Schemas[parameterEnumSchemaReference.Reference.Id]; - var description = enumSchema.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); + var enumSchema = parameterEnumSchemaReference?.Reference.Id != null + ? context.SchemaRepository.Schemas[parameterEnumSchemaReference.Reference.Id] + : null; + var description = enumSchema?.AddEnumValuesDescription(_xEnumNamesAlias, _xEnumDescriptionsAlias, _includeDescriptionFromAttribute, _newLine); if (description != null) { if (parameter.Description == null) From abe61ada3d0cc1d1b6c9b64ae12e56016b1a4b0d Mon Sep 17 00:00:00 2001 From: Ao Rui Date: Mon, 26 Jan 2026 16:27:27 +0800 Subject: [PATCH 3/3] update: update readme. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 097b041..8ae0a64 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ ## Breaking Changes +* Since [v3.0.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/releases/tag/v3.0.0) supports [Swashbuckle.AspNetCore 10.0.0](https://www.nuget.org/packages/Swashbuckle.AspNetCore/) with **breaking changes**. +* * Since [v2.0.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/releases/tag/v2.0.0) supports [Swashbuckle.AspNetCore 5.0.0](https://www.nuget.org/packages/Swashbuckle.AspNetCore/) with **breaking changes**. For old versions see [README_OLD.md](README_OLD.md). @@ -20,7 +22,8 @@ For old versions see [README_OLD.md](README_OLD.md). |Swashbuckle Version|ASP.NET Core|Swagger / OpenAPI Spec.|This extension Version| |:------------------|:-----------|:----------------------|:---------------------| -|[master](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/master/README.md)|>= 2.0.0|2.0, 3.0|[master](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/master/README.md)| +|[master](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/master/README.md)|>= 8.0.0|2.0, 3.0, 3.1|[master](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/master/README.md)| +|[10.0.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/10.0.0/README.md)|>= 8.0.0|2.0, 3.0, 3.1|[v3.0.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/master/README.md)| |[6.1.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.1.5)|>= 2.0.0|2.0, 3.0|[v2.6.1](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/v2.6.1/README.md)| |[6.1.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.1.0)|>= 2.0.0|2.0, 3.0|[v2.6.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/v2.6.0/README.md)| |[6.0.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.0.0)|>= 2.0.0|2.0, 3.0|[v2.5.1](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/v2.5.1/README.md)|