Skip to content

Commit 2f85f8b

Browse files
committed
Add OpenAPI runtime feature to trim related code in Release builds
1 parent 91a056b commit 2f85f8b

11 files changed

+112
-58
lines changed

src/BenchmarksApps.sln

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorUnited", "BenchmarksA
5555
EndProject
5656
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodosApi", "BenchmarksApps\TodosApi\TodosApi.csproj", "{8E1A1F61-43E4-4629-A25B-7E5FA82697D0}"
5757
EndProject
58+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore.OpenApi", "BenchmarksApps\AspNetCore.OpenApi\AspNetCore.OpenApi.csproj", "{28F432A6-1328-4996-91DD-BB1C87F45BF2}"
59+
EndProject
5860
Global
5961
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6062
Debug_Database|Any CPU = Debug_Database|Any CPU
@@ -207,6 +209,14 @@ Global
207209
{8E1A1F61-43E4-4629-A25B-7E5FA82697D0}.Release_Database|Any CPU.Build.0 = Release_Database|Any CPU
208210
{8E1A1F61-43E4-4629-A25B-7E5FA82697D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
209211
{8E1A1F61-43E4-4629-A25B-7E5FA82697D0}.Release|Any CPU.Build.0 = Release|Any CPU
212+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Debug_Database|Any CPU.ActiveCfg = Debug_Database|Any CPU
213+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Debug_Database|Any CPU.Build.0 = Debug_Database|Any CPU
214+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
215+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
216+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Release_Database|Any CPU.ActiveCfg = Release_Database|Any CPU
217+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Release_Database|Any CPU.Build.0 = Release_Database|Any CPU
218+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
219+
{28F432A6-1328-4996-91DD-BB1C87F45BF2}.Release|Any CPU.Build.0 = Release|Any CPU
210220
EndGlobalSection
211221
GlobalSection(SolutionProperties) = preSolution
212222
HideSolutionNode = FALSE
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0-preview.4.*" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<EmbeddedResource Include="ILLink.Substitutions.xml" LogicalName="ILLink.Substitutions.xml" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<linker>
2+
<assembly fullname="AspNetCore.OpenApi">
3+
<type fullname="Microsoft.AspNetCore.OpenApi.OpenApiFeature">
4+
<method signature="System.Boolean get_IsEnabled()" body="stub" value="false"
5+
feature="Microsoft.AspNetCore.OpenApi.OpenApiFeature.IsEnabled" featurevalue="false"/>
6+
</type>
7+
</assembly>
8+
</linker>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Microsoft.AspNetCore.OpenApi;
2+
3+
public static class OpenApiFeature
4+
{
5+
/// <summary>
6+
/// Indicates whether APIs related to OpenAPI/Swagger functionality are enabled.
7+
/// </summary>
8+
/// <remarks>
9+
/// The value of the property is backed by the "Microsoft.AspNetCore.OpenApi.OpenApiFeature.IsEnabled"
10+
/// <see cref="AppContext"/> setting and defaults to <see langword="true"/> if unset.
11+
/// </remarks>
12+
public static bool IsEnabled { get; } =
13+
AppContext.TryGetSwitch(
14+
switchName: "Microsoft.AspNetCore.OpenApi.OpenApiFeature.IsEnabled",
15+
isEnabled: out var value)
16+
? value : true;
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project>
2+
3+
<PropertyGroup>
4+
<OpenApiEnabled Condition=" '$(OpenApiEnabled)' == '' and $(Configuration.StartsWith('Release')) and ('$(OpenApiGenerateDocuments)' != 'true') ">false</OpenApiEnabled>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<RuntimeHostConfigurationOption Include="Microsoft.AspNetCore.OpenApi.OpenApiFeature.IsEnabled"
9+
Condition=" '$(OpenApiEnabled)' != '' "
10+
Value="$(OpenApiEnabled)"
11+
Trim="true" />
12+
</ItemGroup>
13+
14+
</Project>

src/BenchmarksApps/TodosApi/AppSettings.cs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,19 @@ namespace TodosApi;
44

55
internal class AppSettings
66
{
7-
private bool _suppressDbInitialization;
8-
9-
#if GENERATING_OPENAPI_DOC
10-
public AppSettings()
11-
{
12-
GeneratingOpenApiDoc = true;
13-
}
14-
#endif
15-
167
public required string ConnectionString { get; set; }
178

18-
public bool GeneratingOpenApiDoc { get; set; }
19-
209
public string? JwtSigningKey { get; set; }
2110

22-
public bool SuppressDbInitialization
23-
{
24-
get => _suppressDbInitialization || GeneratingOpenApiDoc;
25-
set => _suppressDbInitialization = value;
26-
}
11+
public bool SuppressDbInitialization { get; set; }
2712
}
2813

2914
// Change to using ValidateDataAnnotations once https://github.com/dotnet/runtime/issues/77412 is complete
3015
internal class AppSettingsValidator : IValidateOptions<AppSettings>
3116
{
3217
public ValidateOptionsResult Validate(string? name, AppSettings options)
3318
{
34-
if (string.IsNullOrEmpty(options.ConnectionString) && !options.GeneratingOpenApiDoc)
19+
if (string.IsNullOrEmpty(options.ConnectionString))
3520
{
3621
return ValidateOptionsResult.Fail("""
3722
Connection string not found.

src/BenchmarksApps/TodosApi/DatabaseConfiguration.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ public static IServiceCollection AddDatabase(this IServiceCollection services)
1111
services.AddSingleton(static sp =>
1212
{
1313
var appSettings = sp.GetRequiredService<IOptions<AppSettings>>().Value;
14-
var db = appSettings.GeneratingOpenApiDoc
15-
? default!
16-
: new NpgsqlSlimDataSourceBuilder(appSettings.ConnectionString).Build();
14+
var db = new NpgsqlSlimDataSourceBuilder(appSettings.ConnectionString).Build();
1715

1816
return db;
1917
});

src/BenchmarksApps/TodosApi/DatabaseInitializer.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using Microsoft.AspNetCore.Hosting.Server;
2-
using Microsoft.AspNetCore.Hosting.Server.Features;
3-
using Microsoft.Extensions.Options;
1+
using Microsoft.Extensions.Options;
42
using Npgsql;
53

64
namespace TodosApi;
@@ -15,7 +13,7 @@ public DatabaseInitializer(NpgsqlDataSource db, IOptions<AppSettings> appSetting
1513
{
1614
_db = db;
1715
_logger = logger;
18-
_initDatabase = !appSettings.Value.SuppressDbInitialization && !appSettings.Value.GeneratingOpenApiDoc;
16+
_initDatabase = !appSettings.Value.SuppressDbInitialization;
1917
}
2018

2119
public Task StartAsync(CancellationToken cancellationToken)
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
#if ENABLE_OPENAPI
1+
using Microsoft.AspNetCore.OpenApi;
22
using Microsoft.OpenApi.Models;
3-
#endif
43

54
namespace Microsoft.Extensions.Hosting;
65

76
internal static class OpenApiExtensions
87
{
98
public static IServiceCollection AddOpenApi(this IServiceCollection services)
109
{
11-
#if ENABLE_OPENAPI
12-
services.AddEndpointsApiExplorer();
13-
services.AddSwaggerGen(c =>
10+
if (OpenApiFeature.IsEnabled)
1411
{
15-
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todos API", Version = "v1" });
16-
});
17-
#endif
12+
services.AddEndpointsApiExplorer();
13+
services.AddSwaggerGen(c =>
14+
{
15+
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todos API", Version = "v1" });
16+
});
17+
}
1818
return services;
1919
}
2020
}

src/BenchmarksApps/TodosApi/TodosApi.csproj

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,31 @@
99
<LangVersion>preview</LangVersion>
1010
<UserSecretsId>b8ffb8d3-b768-460b-ac1f-ef267c954c85</UserSecretsId>
1111
<PublishAot>true</PublishAot>
12+
<OpenApiDocumentsDirectory>.\</OpenApiDocumentsDirectory>
13+
<!--<OpenApiEnabled>false</OpenApiEnabled>-->
1214
<!-- Don't use configuration binding generator until bug is fixed: https://github.com/dotnet/runtime/issues/83600 -->
1315
<EnableConfigurationBindingGenerator>false</EnableConfigurationBindingGenerator>
1416
<EnableLogging Condition=" '$(EnableLogging)' == '' and $(Configuration.StartsWith('Debug')) ">true</EnableLogging>
15-
<EnableOpenApi Condition=" ('$(EnableOpenApi)' == '' and $(Configuration.StartsWith('Debug'))) or '$(OpenApiGenerateDocuments)' == 'true' ">true</EnableOpenApi>
1617
<DefineConstants Condition=" '$(EnableLogging)' == 'true' ">$(DefineConstants);ENABLE_LOGGING</DefineConstants>
17-
<DefineConstants Condition=" '$(EnableOpenApi)' == 'true' ">$(DefineConstants);ENABLE_OPENAPI</DefineConstants>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
21-
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0-preview.*" />
22-
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0-preview.*" />
21+
<ProjectReference Include="..\AspNetCore.OpenApi\AspNetCore.OpenApi.csproj" />
22+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0-preview.4.*" />
23+
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.0-preview.4.*">
24+
<PrivateAssets>all</PrivateAssets>
25+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
26+
</PackageReference>
27+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0-preview.4.*" />
2328
<PackageReference Include="Npgsql" Version="8.0.0-preview.3" />
29+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
30+
</ItemGroup>
31+
32+
<ItemGroup>
2433
<Content Update="appSettings.Development.json" CopyToPublishDirectory="false" />
2534
<!-- Workaround for https://github.com/dotnet/aspnetcore/issues/47941 -->
2635
<IlcArg Include="--nopreinitstatics" />
2736
</ItemGroup>
2837

29-
<PropertyGroup Condition=" '$(EnableOpenApi)' == 'true' ">
30-
<!-- Force OpenAPI doc generation during build to off as we're directly referencing Microsoft.Extensions.ApiDescription.Server -->
31-
<OpenApiGenerateDocuments Condition=" '$(OpenApiGenerateDocuments)' == '' ">false</OpenApiGenerateDocuments>
32-
<DefineConstants Condition=" '$(OpenApiGenerateDocuments)' == 'true' ">$(DefineConstants);GENERATING_OPENAPI_DOC</DefineConstants>
33-
</PropertyGroup>
34-
35-
<ItemGroup Condition=" '$(EnableOpenApi)' == 'true' ">
36-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
37-
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.0-preview.*" />
38-
</ItemGroup>
38+
<Import Project="..\AspNetCore.OpenApi\build\AspNetCore.OpenApi.targets" />
3939
</Project>

src/BenchmarksApps/TodosApi/TodosApi.openapi.json renamed to src/BenchmarksApps/TodosApi/TodosApi.json

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"/throw": {
99
"get": {
1010
"tags": [
11-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
11+
"TodosApi"
1212
],
1313
"responses": {
1414
"200": {
@@ -20,7 +20,7 @@
2020
"/api/todos": {
2121
"get": {
2222
"tags": [
23-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
23+
"TodosApi"
2424
],
2525
"operationId": "GetAllTodos",
2626
"responses": {
@@ -41,7 +41,7 @@
4141
},
4242
"post": {
4343
"tags": [
44-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
44+
"TodosApi"
4545
],
4646
"operationId": "CreateTodo",
4747
"requestBody": {
@@ -65,7 +65,7 @@
6565
"/api/todos/complete": {
6666
"get": {
6767
"tags": [
68-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
68+
"TodosApi"
6969
],
7070
"operationId": "GetCompleteTodos",
7171
"responses": {
@@ -88,7 +88,7 @@
8888
"/api/todos/incomplete": {
8989
"get": {
9090
"tags": [
91-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
91+
"TodosApi"
9292
],
9393
"operationId": "GetIncompleteTodos",
9494
"responses": {
@@ -111,14 +111,15 @@
111111
"/api/todos/{id}": {
112112
"get": {
113113
"tags": [
114-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
114+
"TodosApi"
115115
],
116116
"operationId": "GetTodoById",
117117
"parameters": [
118118
{
119119
"name": "id",
120120
"in": "path",
121121
"required": true,
122+
"style": "simple",
122123
"schema": {
123124
"type": "integer",
124125
"format": "int32"
@@ -133,14 +134,15 @@
133134
},
134135
"put": {
135136
"tags": [
136-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
137+
"TodosApi"
137138
],
138139
"operationId": "UpdateTodo",
139140
"parameters": [
140141
{
141142
"name": "id",
142143
"in": "path",
143144
"required": true,
145+
"style": "simple",
144146
"schema": {
145147
"type": "integer",
146148
"format": "int32"
@@ -166,14 +168,15 @@
166168
},
167169
"delete": {
168170
"tags": [
169-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
171+
"TodosApi"
170172
],
171173
"operationId": "DeleteTodo",
172174
"parameters": [
173175
{
174176
"name": "id",
175177
"in": "path",
176178
"required": true,
179+
"style": "simple",
177180
"schema": {
178181
"type": "integer",
179182
"format": "int32"
@@ -190,21 +193,23 @@
190193
"/api/todos/find": {
191194
"get": {
192195
"tags": [
193-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
196+
"TodosApi"
194197
],
195198
"operationId": "FindTodo",
196199
"parameters": [
197200
{
198201
"name": "title",
199202
"in": "query",
200203
"required": true,
204+
"style": "form",
201205
"schema": {
202206
"type": "string"
203207
}
204208
},
205209
{
206210
"name": "isComplete",
207211
"in": "query",
212+
"style": "form",
208213
"schema": {
209214
"type": "boolean"
210215
}
@@ -220,14 +225,15 @@
220225
"/api/todos/{id}/mark-complete": {
221226
"put": {
222227
"tags": [
223-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
228+
"TodosApi"
224229
],
225230
"operationId": "MarkComplete",
226231
"parameters": [
227232
{
228233
"name": "id",
229234
"in": "path",
230235
"required": true,
236+
"style": "simple",
231237
"schema": {
232238
"type": "integer",
233239
"format": "int32"
@@ -244,14 +250,15 @@
244250
"/api/todos/{id}/mark-incomplete": {
245251
"put": {
246252
"tags": [
247-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
253+
"TodosApi"
248254
],
249255
"operationId": "MarkIncomplete",
250256
"parameters": [
251257
{
252258
"name": "id",
253259
"in": "path",
254260
"required": true,
261+
"style": "simple",
255262
"schema": {
256263
"type": "integer",
257264
"format": "int32"
@@ -268,7 +275,7 @@
268275
"/api/todos/delete-all": {
269276
"delete": {
270277
"tags": [
271-
"TodosApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
278+
"TodosApi"
272279
],
273280
"operationId": "DeleteAll",
274281
"responses": {

0 commit comments

Comments
 (0)