Skip to content

Commit 2698d41

Browse files
Rick-Andersonmartincostellomikekistler
authored
Fix and Edit build time OpenAPI/ra/b (#33361)
* Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Fix and Edit build time OpenAPI/ra * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Add SqlDB to skip * Apply suggestions from code review Co-authored-by: Martin Costello <[email protected]> * Add SqlDB to skip * react to feedback * react to feedback * react to feedback * react to feedback * react to feedback * react to feedback * react to feedback * react to feedback * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * fix script * react to feedback V2 * react to feedback V2 * react to feedback V2 * react to feedback V2 * react to feedback V2 * Apply suggestions from code review Co-authored-by: Mike Kistler <[email protected]> * Fix toc merge conflict * add comment it's V9 --------- Co-authored-by: Martin Costello <[email protected]> Co-authored-by: Mike Kistler <[email protected]>
1 parent 90ee5ed commit 2698d41

File tree

12 files changed

+547
-58
lines changed

12 files changed

+547
-58
lines changed

aspnetcore/fundamentals/openapi/aspnetcore-openapi.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ ms.custom: mvc
88
ms.date: 5/22/2024
99
uid: fundamentals/openapi/aspnetcore-openapi
1010
---
11+
<!-- backup writer.sms.author: tdykstra and rick-anderson -->
12+
1113
# Work with OpenAPI documents
1214

1315
:::moniker range=">= aspnetcore-9.0"
@@ -67,7 +69,7 @@ The following code:
6769
* Adds OpenAPI services.
6870
* Enables the endpoint for viewing the OpenAPI document in JSON format.
6971

70-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_first&highlight=3,7)]
72+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_first&highlight=3,7)]
7173

7274
Launch the app and navigate to `https://localhost:<port>/openapi/v1.json` to view the generated OpenAPI document.
7375

@@ -400,13 +402,13 @@ Because the OpenAPI document is served via a route handler endpoint, any customi
400402

401403
The OpenAPI endpoint doesn't enable any authorization checks by default. However, it's possible to limit access to the OpenAPI document. For example, in the following code, access to the OpenAPI document is limited to those with the `tester` role:
402404

403-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_mapopenapiwithauth)]
405+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_mapopenapiwithauth)]
404406

405407
#### Cache generated OpenAPI document
406408

407409
The OpenAPI document is regenerated every time a request to the OpenAPI endpoint is sent. Regeneration enables transformers to incorporate dynamic application state into their operation. For example, regenerating a request with details of the HTTP context. When applicable, the OpenAPI document can be cached to avoid executing the document generation pipeline on each HTTP request.
408410

409-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_mapopenapiwithcaching)]
411+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_mapopenapiwithcaching)]
410412

411413
<a name="transformers"></a>
412414

@@ -434,13 +436,13 @@ Transformers can be registered onto the document via the `AddDocumentTransformer
434436
* Register a document transformer using a DI-activated `IOpenApiDocumentTransformer`.
435437
* Register an operation transformer using a delegate.
436438

437-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_transUse&highlight=8-13)]
439+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transUse&highlight=8-13)]
438440

439441
### Execution order for transformers
440442

441443
Transformers execute in first-in first-out order based on registration. In the following snippet, the document transformer has access to the modifications made by the operation transformer:
442444

443-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_transInOut&highlight=3-9)]
445+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transInOut&highlight=3-9)]
444446

445447
### Use document transformers
446448

@@ -452,18 +454,18 @@ Document transformers have access to a context object that includes:
452454

453455
Document transformers also can mutate the OpenAPI document that is generated. The following example demonstrates a document transformer that adds some information about the API to the OpenAPI document.
454456

455-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_documenttransformer1)]
457+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer1)]
456458

457459
Service-activated document transformers can utilize instances from DI to modify the app. The following sample demonstrates a document transformer that uses the `IAuthenticationSchemeProvider` service from the authentication layer. It checks if any JWT bearer-related schemes are registered in the app and adds them to the OpenAPI document's top level:
458460

459-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_documenttransformer2)]
461+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer2)]
460462

461463
Document transformers are unique to the document instance they're associated with. In the following example, a transformer:
462464

463465
* Registers authentication-related requirements to the `internal` document.
464466
* Leaves the `public` document unmodified.
465467

466-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_multidoc_operationtransformer1)]
468+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_multidoc_operationtransformer1)]
467469

468470
### Use operation transformers
469471

@@ -480,7 +482,7 @@ Operation transformers have access to a context object which contains:
480482

481483
For example, the following operation transformer adds `500` as a response status code supported by all operations in the document.
482484

483-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_operationtransformer1)]
485+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_operationtransformer1)]
484486

485487
## Using the generated OpenAPI document
486488

@@ -494,13 +496,13 @@ The `Swashbuckle.AspNetCore.SwaggerUi` package provides a bundle of Swagger UI's
494496

495497
Enable the swagger-ui middleware with a reference to the OpenAPI route registered earlier. To limit information disclosure and security vulnerability, ***only enable Swagger UI in development environments.***
496498

497-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_swaggerui)]
499+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_swaggerui)]
498500

499501
### Using Scalar for interactive API documentation
500502

501503
[Scalar](https://scalar.com/) is an open-source interactive document UI for OpenAPI. Scalar can integrate with the OpenAPI endpoint provided by ASP.NET Core. To configure Scalar, install the `Scalar.AspNetCore` package.
502504

503-
[!code-csharp[](~/fundamentals/minimal-apis/9.0-samples/WebMinOpenApi/Program.cs?name=snippet_openapiwithscalar)]
505+
[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_openapiwithscalar)]
504506

505507
### Lint generated OpenAPI documents with Spectral
506508

aspnetcore/fundamentals/openapi/buildtime-openapi.md

Lines changed: 76 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,27 @@ title: Generate OpenAPI documents at build time
33
author: captainsafia
44
description: Learn how to generate OpenAPI documents in your application's build step
55
ms.author: safia
6-
monikerRange: '>= aspnetcore-6.0'
6+
monikerRange: '>= aspnetcore-9.0'
77
ms.custom: mvc
88
ms.date: 8/13/2024
99
uid: fundamentals/openapi/buildtime-openapi
1010
---
11+
<!-- Per this comment by Mike, Question for @captainsafia: This doc currently says aspnetcore-6.0 and later. Should we make this just 9.0 and later, since 8.0 and earlier involve Swashbuckle or NSwag generating the doc?
12+
I made it 9.0
13+
https://github.com/dotnet/AspNetCore.Docs/pull/33361#discussion_r1778546753 -->
14+
<!-- backup writer.sms.author: tdykstra and rick-anderson -->
1115

1216
# Generate OpenAPI documents at build-time
1317

14-
In a typical web applications, OpenAPI documents are generated at run-time and served via an HTTP request to the application server.
18+
In typical web apps, OpenAPI documents are generated at run-time and served via an HTTP request to the app server.
1519

16-
In some scenarios, it is helpful to generate the OpenAPI document during the application's build step. These scenarios including:
20+
Generating OpenAPI documentation during the app's build step can be useful for documentation that is:
1721

18-
- Generating OpenAPI documentation that is committed into source control
19-
- Generating OpenAPI documentation that is used for spec-based integration testing
20-
- Generating OpenAPI documentation that is served statically from the web server
22+
- Committed into source control.
23+
- Used for spec-based integration testing.
24+
- Served statically from the web server.
2125

22-
To add support for generating OpenAPI documents at build time, install the `Microsoft.Extensions.ApiDescription.Server` package:
26+
To add support for generating OpenAPI documents at build time, install the [`Microsoft.Extensions.ApiDescription.Server`](https://www.nuget.org/packages/Microsoft.Extensions.ApiDescription.Server) NuGet package:
2327

2428
### [Visual Studio](#tab/visual-studio)
2529

@@ -36,65 +40,92 @@ Run the following command in the directory that contains the project file:
3640
```dotnetcli
3741
dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease
3842
```
43+
3944
---
4045

41-
Upon installation, this package will automatically generate the Open API document(s) associated with the application during build and populate them into the application's output directory.
46+
The `Microsoft.Extensions.ApiDescription.Server` package automatically generates the Open API document(s) associated with the app during build and places them in the app's `obj` directory:
47+
48+
Consider a template created API app named `MyTestApi`:
49+
50+
### [Visual Studio](#tab/visual-studio)
51+
52+
The Output tab in Visual Studio when building the app includes information similar to the following:
53+
54+
```text
55+
1>Generating document named 'v1'.
56+
1>Writing document named 'v1' to 'MyProjectPath/obj/MyTestApi.json'.
57+
```
58+
59+
### [.NET CLI](#tab/net-cli)
60+
61+
The following commands build the app and display the generated OpenAPI document:
4262

4363
```cli
4464
$ dotnet build
45-
$ cat bin/Debub/net9.0/{ProjectName}.json
65+
$ cat obj/MyTestApi.json
4666
```
4767

48-
## Customizing build-time document generation
68+
---
4969

50-
### Modifying the output directory of the generated Open API file
70+
The generated `obj/{MyProjectName}.json` file contains the [OpenAPI version, title, endpoints, and more](https://learn.openapis.org/specification/structure.html). The first few lines of `obj/MyTestApi.json` file:
5171

52-
By default, the generated OpenAPI document will be emitted to the application's output directory. To modify the location of the emitted file, set the target path in the `OpenApiDocumentsDirectory` property.
72+
:::code language="json" source="~/fundamentals/openapi/samples/9.x/BuildTime/csproj/MyTestApi.json" range="1-15" highlight="4-5":::
5373

54-
```xml
55-
<PropertyGroup>
56-
<OpenApiDocumentsDirectory>./</OpenApiDocumentsDirectory>
57-
</PropertyGroup>
58-
```
74+
## Customize build-time document generation
5975

60-
The value of `OpenApiDocumentsDirectory` is resolved relative to the project file. Using the `./` value above will emit the OpenAPI document in the same directory as the project file.
76+
Build-time document generation can be customized with properties added to the project file. [dotnet](/dotnet/core/tools/) parses the `ApiDescription.Server` properties in the project file and provides the property and values as arguments to the build-time document generator. The following properties are available and explained in the following sections:
6177

62-
### Modifying the output file name
78+
:::code language="xml" source="~/fundamentals/openapi/samples/9.x/BuildTime/csproj/MyTestApi.csproj.php" id="snippet_all" highlight="2-4":::
6379

64-
By default, the generated OpenAPI document will have the same name as the application's project file. To modify the name of the emitted file, set the `--file-name` argument in the `OpenApiGenerateDocumentsOptions` property.
80+
### Modify the output directory of the generated Open API file
6581

66-
```xml
67-
<PropertyGroup>
68-
<OpenApiGenerateDocumentsOptions>--file-name my-open-api</OpenApiGenerateDocumentsOptions>
69-
</PropertyGroup>
70-
```
82+
By default, the generated OpenAPI document is generated in the `obj` directory. The value of the `OpenApiDocumentsDirectory` property:
7183

72-
### Selecting the OpenAPI document to generate
84+
* Sets the location of the generated file.
85+
* Is resolved relative to the project file.
7386

74-
Some applications may be configured to emit multiple OpenAPI documents, for various versions of an API or to distinguish between public and internal APIs. By default, the build-time document generator will emit files for all documents that are configured in an application. To only emit for a single document name, set the `--document-name` argument in the `OpenApiGenerateDocumentsOptions` property.
87+
The following example generates the OpenAPI document in the same directory as the project file:
7588

76-
```xml
77-
<PropertyGroup>
78-
<OpenApiGenerateDocumentsOptions>--document-name v2</OpenApiGenerateDocumentsOptions>
79-
</PropertyGroup>
80-
```
89+
:::code language="xml" source="~/fundamentals/openapi/samples/9.x/BuildTime/csproj/MyTestApi.csproj.php" id="snippet_1" highlight="2":::
8190

82-
## Customizing run-time behavior during build-time document generation
91+
In the following example, the OpenAPI document is generated in the `MyOpenApiDocs` directory, which is a sibling directory to the project directory:
8392

84-
Under the hood, build-time OpenAPI document generation functions by launching the application's entrypoint with an inert server implementation. This is a requirement to produce accurate OpenAPI documents since all information in the OpenAPI document cannot be statically analyzed. Because the application's entrypoint is invoked, any logic in the applications' startup will be invoked. This includes code that injects services into the DI container or reads from configuration. In some scenarios, it's necessary to restrict the codepaths that will run when the application's entry point is being invoked from build-time document generation. These scenarios include:
93+
:::code language="xml" source="~/fundamentals/openapi/samples/9.x/BuildTime/csproj/MyTestApi.csproj.php" id="snippet2" highlight="2":::
8594

86-
- Not reading from certain configuration strings
87-
- Not registering database-related services
95+
### Modify the output file name
8896

89-
In order to restrict these codepaths from being invoked by the build-time generation pipeline, they can be conditioned behind a check of the entry assembly like so:
97+
By default, the generated OpenAPI document has the same name as the app's project file. To modify the name of the generated file, set the `--file-name` argument in the `OpenApiGenerateDocumentsOptions` property:
9098

91-
```csharp
92-
using System.Reflection;
99+
:::code language="xml" source="~/fundamentals/openapi/samples/9.x/BuildTime/csproj/MyTestApi.csproj.php" id="snippet_file_name" highlight="2":::
93100

94-
var builder = WebApplication.CreateBuilder();
101+
The preceding markup configures creation of the `obj/my-open-api.json` file.
95102

96-
if (Assembly.GetEntryAssembly()?.GetName().Name != "GetDocument.Insider")
97-
{
98-
builders.Services.AddDefaults();
99-
}
100-
```
103+
### Select the OpenAPI document to generate
104+
105+
Some apps may be configured to generate multiple OpenAPI documents, for example:
106+
107+
* For different versions of an API.
108+
* To distinguish between public and internal APIs.
109+
110+
By default, the build-time document generator creates files for all documents that are configured in an app. To generate for a single document only, set the `--document-name` argument in the `OpenApiGenerateDocumentsOptions` property:
111+
112+
:::code language="xml" source="~/fundamentals/openapi/samples/9.x/BuildTime/csproj/MyTestApi.csproj.php" id="snippet_doc_name":::
113+
114+
<!-- comment out this section until it's sorted
115+
## Customize run-time behavior during build-time document generation
116+
117+
OpenAPI document generation at build-time works by starting the app’s entry point with a temporary background server. This approach is necessary to produce accurate OpenAPI documents, as not all information can be statically analyzed. When the app’s entry point is invoked, any logic in the app’s startup is executed, including code that injects services into the DI container or reads from configuration.
118+
119+
In some scenarios, it's important to restrict certain code paths when the app's entry point is invoked during build-time document generation. These scenarios include:
120+
121+
- Not reading from specific configuration strings.
122+
- Not registering database-related services.
123+
124+
To prevent these code paths from being invoked by the build-time generation pipeline, they can be conditioned behind a check of the entry assembly:
125+
126+
:::code language="csharp" source="~/fundamentals/openapi/samples/9.x/BuildTime/Skip.cs" id="snippet_1" highlight="7-12":::
127+
-->
128+
129+
## OpenAPI document cleanup
130+
131+
The generated OpenAPI documents are not cleaned up by `dotnet clean` or **Build > Clean Solution** in Visual Studio. To remove the generated OpenAPI documents, delete the `obj` directory or the directory specified by the `OpenApiDocumentsDirectory` property.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0-rc.1.24412.15" />
11+
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="9.0.0-rc.2.24420.12">
12+
<PrivateAssets>all</PrivateAssets>
13+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14+
</PackageReference>
15+
</ItemGroup>
16+
17+
<PropertyGroup>
18+
<DocNameV2>true</DocNameV2>
19+
</PropertyGroup>
20+
21+
<PropertyGroup Condition=" '$(DocNameV2)' == 'true' ">
22+
<OpenApiGenerateDocumentsOptions>--document-name v2</OpenApiGenerateDocumentsOptions>
23+
</PropertyGroup>
24+
25+
</Project>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@MyTestApi_HostAddress = http://localhost:5276
2+
3+
GET {{MyTestApi_HostAddress}}/weatherforecast/
4+
Accept: application/json
5+
6+
###
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var builder = WebApplication.CreateBuilder(args);
2+
3+
builder.Services.AddOpenApi();
4+
builder.Services.AddOpenApi("v2");
5+
6+
var app = builder.Build();
7+
8+
if (app.Environment.IsDevelopment())
9+
{
10+
app.MapOpenApi();
11+
}
12+
13+
app.UseHttpsRedirection();
14+
15+
var summaries = new[]
16+
{
17+
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
18+
};
19+
20+
app.MapGet("/weatherforecast", () =>
21+
{
22+
var forecast = Enumerable.Range(1, 5).Select(index =>
23+
new WeatherForecast
24+
(
25+
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
26+
Random.Shared.Next(-20, 55),
27+
summaries[Random.Shared.Next(summaries.Length)]
28+
))
29+
.ToArray();
30+
return forecast;
31+
})
32+
.WithName("GetWeatherForecast");
33+
34+
app.MapGet("/v2/weatherforecast", (HttpContext httpContext) =>
35+
{
36+
var forecast = Enumerable.Range(1, 5).Select(index =>
37+
new WeatherForecast
38+
(
39+
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
40+
Random.Shared.Next(-20, 55),
41+
summaries[Random.Shared.Next(summaries.Length)]
42+
))
43+
.ToArray();
44+
return forecast;
45+
})
46+
.WithGroupName("v2");
47+
48+
app.Run();
49+
50+
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
51+
{
52+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#if NEVER
2+
// <snippet_1>
3+
using ControllerApi.Data;
4+
using Microsoft.EntityFrameworkCore;
5+
using System.Reflection;
6+
7+
var builder = WebApplication.CreateBuilder(args);
8+
9+
if (Assembly.GetEntryAssembly()?.GetName().Name != "MyTestApi")
10+
{
11+
builder.Services.AddDbContext<ControllerApiContext>(options =>
12+
options.UseSqlServer(builder.Configuration.GetConnectionString("ControllerApiContext")
13+
?? throw new InvalidOperationException("Connection string 'ControllerApiContext' not found.")));
14+
}
15+
16+
builder.Services.AddControllers();
17+
builder.Services.AddOpenApi();
18+
19+
var app = builder.Build();
20+
21+
if (app.Environment.IsDevelopment())
22+
{
23+
app.MapOpenApi();
24+
}
25+
26+
app.UseHttpsRedirection();
27+
28+
app.UseAuthorization();
29+
30+
app.MapControllers();
31+
32+
app.Run();
33+
// </snippet_1>
34+
#endif
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)