Skip to content

Commit

Permalink
Add .NET 8 support (#2799)
Browse files Browse the repository at this point in the history
- Add support for .NET 8.
- Bump version to `6.6.0`.
- Add WeatherForecast .NET 8 Minimal API test application to resolve #2791.
- Add test for #2771 for `DateOnly` values.
- Use `JsonOptions` for Minimal APIs in .NET 8+ if not available from MVC.
- Use `JsonSerializerOptions.Default` where available.
- Use the Regex source generator for .NET 7+ to resolve #2794.
- Refactor JsonSerializerOptions resolution
- Make `ISerializerDataContractResolver` a singleton by default.
  • Loading branch information
martincostello committed May 12, 2024
1 parent 35cfb1c commit 3ce85e7
Show file tree
Hide file tree
Showing 52 changed files with 395 additions and 87 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseArtifactsOutput>true</UseArtifactsOutput>
<VersionPrefix>6.5.1</VersionPrefix>
<VersionPrefix>6.6.0</VersionPrefix>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
</PropertyGroup>
<PropertyGroup Condition=" '$(GITHUB_ACTIONS)' != '' ">
Expand Down
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.28" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" />
<PackageVersion Include="Microsoft.AspNetCore.Routing" Version="2.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0" />
Expand All @@ -23,7 +24,7 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="Nswag.MSbuild" Version="13.17.0" />
<PackageVersion Include="Nswag.MSbuild" Version="14.0.7" />
<PackageVersion Include="ReportGenerator" Version="5.2.2" />
<PackageVersion Include="System.Text.Json" Version="4.6.0" />
<PackageVersion Include="xunit" Version="2.7.0" />
Expand Down
19 changes: 19 additions & 0 deletions Swashbuckle.AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomDocumentSerializer", "test\WebSites\CustomDocumentSerializer\CustomDocumentSerializer.csproj", "{B6037A37-4A4F-438D-B18A-0C9D1408EAB2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "test\WebSites\WebApi\WebApi.csproj", "{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -253,6 +255,22 @@ Global
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Release|Any CPU.Build.0 = Release|Any CPU
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Release|x64.ActiveCfg = Release|Any CPU
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Release|x64.Build.0 = Release|Any CPU
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Release|x86.ActiveCfg = Release|Any CPU
{6EA75DA8-9B1F-468E-9425-37F01A129B0F}.Release|x86.Build.0 = Release|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x64.ActiveCfg = Debug|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x64.Build.0 = Debug|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x86.ActiveCfg = Debug|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x86.Build.0 = Debug|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|Any CPU.Build.0 = Release|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x64.ActiveCfg = Release|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x64.Build.0 = Release|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x86.ActiveCfg = Release|Any CPU
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -295,6 +313,7 @@ Global
{3BA087DA-788C-43D6-9D8B-1EF017014A4A} = {FA1B4021-0A97-4F68-B966-148191F6AAA8}
{A0EC16BE-C520-4FCF-BB54-2D79CD255F00} = {3BA087DA-788C-43D6-9D8B-1EF017014A4A}
{B6037A37-4A4F-438D-B18A-0C9D1408EAB2} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;annotations</PackageTags>
<SignAssembly>true</SignAssembly>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<IsTestProject>false</IsTestProject>
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;openapi;test-first;spec-first;testing;aspnetcore;xunit</PackageTags>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;openapi;test-first;spec-first;testing;aspnetcore</PackageTags>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -34,4 +34,8 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" VersionOverride="7.0.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" VersionOverride="8.0.0" />
</ItemGroup>

</Project>
3 changes: 1 addition & 2 deletions src/Swashbuckle.AspNetCore.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -102,7 +101,7 @@ public static int Main(string[] args)
? Path.Combine(Directory.GetCurrentDirectory(), arg1)
: null;
using (Stream stream = (outputPath != null ? File.OpenWrite(outputPath) : Console.OpenStandardOutput()))
using (Stream stream = outputPath != null ? File.OpenWrite(outputPath) : Console.OpenStandardOutput())
using (var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture))
{
IOpenApiWriter writer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<OutputType>Exe</OutputType>
<PackAsTool>true</PackAsTool>
<PackageId>Swashbuckle.AspNetCore.Cli</PackageId>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<ToolCommandName>swagger</ToolCommandName>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Description>Swagger Generator opt-in component to support Newtonsoft.Json serializer behaviors</Description>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;newtonsoft</PackageTags>
<SignAssembly>true</SignAssembly>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand All @@ -32,4 +32,8 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" VersionOverride="7.0.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" VersionOverride="8.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;redoc</PackageTags>
<SignAssembly>true</SignAssembly>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore</PackageTags>
<SignAssembly>true</SignAssembly>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.ApiDescriptions;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
Expand All @@ -16,7 +15,7 @@ public static class SwaggerGenServiceCollectionExtensions
Action<SwaggerGenOptions> setupAction = null)
{
// Add Mvc convention to ensure ApiExplorer is enabled for all actions
services.Configure<MvcOptions>(c =>
services.Configure<AspNetCore.Mvc.MvcOptions>(c =>
c.Conventions.Add(new SwaggerApplicationConvention()));

// Register custom configurators that takes values from SwaggerGenOptions (i.e. high level config)
Expand All @@ -30,15 +29,10 @@ public static class SwaggerGenServiceCollectionExtensions
services.TryAddTransient(s => s.GetRequiredService<IOptions<SwaggerGeneratorOptions>>().Value);
services.TryAddTransient<ISchemaGenerator, SchemaGenerator>();
services.TryAddTransient(s => s.GetRequiredService<IOptions<SchemaGeneratorOptions>>().Value);
services.TryAddTransient<ISerializerDataContractResolver>(s =>
services.AddSingleton<JsonSerializerOptionsProvider>();
services.TryAddSingleton<ISerializerDataContractResolver>(s =>
{
#if (!NETSTANDARD2_0)
var serializerOptions = s.GetService<IOptions<JsonOptions>>()?.Value?.JsonSerializerOptions
?? new JsonSerializerOptions();
#else
var serializerOptions = new JsonSerializerOptions();
#endif
var serializerOptions = s.GetRequiredService<JsonSerializerOptionsProvider>().Options;
return new JsonSerializerDataContractResolver(serializerOptions);
});

Expand All @@ -56,5 +50,46 @@ public static class SwaggerGenServiceCollectionExtensions
{
services.Configure(setupAction);
}

private sealed class JsonSerializerOptionsProvider
{
private readonly IServiceProvider _serviceProvider;
private JsonSerializerOptions _options;

public JsonSerializerOptionsProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public JsonSerializerOptions Options => _options ??= ResolveOptions();

private JsonSerializerOptions ResolveOptions()
{
JsonSerializerOptions serializerOptions;

/*
* First try to get the options configured for MVC,
* then try to get the options configured for Minimal APIs if available,
* then try the default JsonSerializerOptions if available,
* otherwise create a new instance as a last resort as this is an expensive operation.
*/
#if !NETSTANDARD2_0
serializerOptions =
_serviceProvider.GetService<IOptions<AspNetCore.Mvc.JsonOptions>>()?.Value?.JsonSerializerOptions
#if NET8_0_OR_GREATER
?? _serviceProvider.GetService<IOptions<AspNetCore.Http.Json.JsonOptions>>()?.Value?.SerializerOptions
#endif
#if NET7_0_OR_GREATER
?? JsonSerializerOptions.Default;
#else
?? new JsonSerializerOptions();
#endif
#else
serializerOptions = new JsonSerializerOptions();
#endif

return serializerOptions;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore</PackageTags>
<SignAssembly>true</SignAssembly>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@

namespace Swashbuckle.AspNetCore.SwaggerGen
{
public static class XmlCommentsTextHelper
public static partial class XmlCommentsTextHelper
{
private static Regex RefTagPattern = new Regex(@"<(see|paramref) (name|cref|langword)=""([TPF]{1}:)?(?<display>.+?)"" ?/>");
private static Regex CodeTagPattern = new Regex(@"<c>(?<display>.+?)</c>");
private static Regex MultilineCodeTagPattern = new Regex(@"<code>(?<display>.+?)</code>", RegexOptions.Singleline);
private static Regex ParaTagPattern = new Regex(@"<para>(?<display>.+?)</para>", RegexOptions.Singleline);
private static Regex HrefPattern = new(@"<see href=\""(.*)\"">(.*)<\/see>");

public static string Humanize(string text)
{
if (text == null)
Expand Down Expand Up @@ -91,33 +85,67 @@ private static string GetCommonLeadingWhitespace(string[] lines)

private static string HumanizeRefTags(this string text)
{
return RefTagPattern.Replace(text, (match) => match.Groups["display"].Value);
return RefTag().Replace(text, (match) => match.Groups["display"].Value);
}

private static string HumanizeHrefTags(this string text)
{
return HrefPattern.Replace(text, m => $"[{m.Groups[2].Value}]({m.Groups[1].Value})");
return HrefTag().Replace(text, m => $"[{m.Groups[2].Value}]({m.Groups[1].Value})");
}

private static string HumanizeCodeTags(this string text)
{
return CodeTagPattern.Replace(text, (match) => "`" + match.Groups["display"].Value + "`");
return CodeTag().Replace(text, (match) => "`" + match.Groups["display"].Value + "`");
}

private static string HumanizeMultilineCodeTags(this string text)
{
return MultilineCodeTagPattern.Replace(text, (match) => "```" + match.Groups["display"].Value + "```");
return MultilineCodeTag().Replace(text, (match) => "```" + match.Groups["display"].Value + "```");
}

private static string HumanizeParaTags(this string text)
{
return ParaTagPattern.Replace(text, (match) => "<br>" + match.Groups["display"].Value);
return ParaTag().Replace(text, (match) => "<br>" + match.Groups["display"].Value);
}

private static string DecodeXml(this string text)
{
return WebUtility.HtmlDecode(text);
}

private const string RefTagPattern = @"<(see|paramref) (name|cref|langword)=""([TPF]{1}:)?(?<display>.+?)"" ?/>";
private const string CodeTagPattern = @"<c>(?<display>.+?)</c>";
private const string MultilineCodeTagPattern = @"<code>(?<display>.+?)</code>";
private const string ParaTagPattern = @"<para>(?<display>.+?)</para>";
private const string HrefPattern = @"<see href=\""(.*)\"">(.*)<\/see>";

#if NET7_0_OR_GREATER
[GeneratedRegex(RefTagPattern)]
private static partial Regex RefTag();

[GeneratedRegex(CodeTagPattern)]
private static partial Regex CodeTag();

[GeneratedRegex(MultilineCodeTagPattern, RegexOptions.Singleline)]
private static partial Regex MultilineCodeTag();

[GeneratedRegex(ParaTagPattern, RegexOptions.Singleline)]
private static partial Regex ParaTag();

[GeneratedRegex(HrefPattern)]
private static partial Regex HrefTag();
#else
private static readonly Regex _refTag = new(RefTagPattern);
private static readonly Regex _codeTag = new(CodeTagPattern);
private static readonly Regex _multilineCodeTag = new(MultilineCodeTagPattern, RegexOptions.Singleline);
private static readonly Regex _paraTag = new(ParaTagPattern, RegexOptions.Singleline);
private static readonly Regex _hrefTag = new(HrefPattern);

private static Regex RefTag() => _refTag;
private static Regex CodeTag() => _codeTag;
private static Regex MultilineCodeTag() => _multilineCodeTag;
private static Regex ParaTag() => _paraTag;
private static Regex HrefTag() => _hrefTag;
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);1591</NoWarn>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore</PackageTags>
<SignAssembly>true</SignAssembly>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<PackageId>Swashbuckle.AspNetCore</PackageId>
<PackageTags>swagger;documentation;discovery;help;webapi;aspnet;aspnetcore</PackageTags>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>

<Target Name="PopulateNuspec">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Xunit;
using Swashbuckle.AspNetCore.SwaggerGen;
using Xunit;

namespace Swashbuckle.AspNetCore.Annotations.Test
{
Expand Down Expand Up @@ -101,9 +102,12 @@ public void Apply_DelegatesToSpecifiedFilter_IfTypeDecoratedWithFilterAttribute(
Assert.NotEmpty(schema.Extensions);
}

private AnnotationsSchemaFilter Subject()
private static AnnotationsSchemaFilter Subject()
{
return new AnnotationsSchemaFilter(null);
// A service provider is required from .NET 8 onwards.
// See https://learn.microsoft.com/dotnet/core/compatibility/extensions/8.0/activatorutilities-createinstance-null-provider.
var serviceProvider = new ServiceCollection().BuildServiceProvider();
return new AnnotationsSchemaFilter(serviceProvider);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 3ce85e7

Please sign in to comment.