Implementing an ASP.NET Core API with .NET 9 and OpenAPI

This post implements a basic ASP.NET Core API using .NET 9 and the Microsoft OpenAPI implementation. The OpenAPI Nuget package supports both Controller based APIs and minimal APIs. Until now, we used excellent solutions like NSwag to produce the API schemas which can be used to auto-generate client code.

Code: https://github.com/damienbod/WebApiOpenApi

Setup

A .NET 9 project can be created using the .NET Web API templates. The required SDKs or Visual Studio version needs to be installed. The OpenAPI Json definitions can be created using the Microsoft.AspNetCore.OpenApi Nuget package. Microsoft learn docs have all the different possibilities for implementing this depending on your preferred development environment. Here’s an example using powershell:

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

JWT OAuth authorization is used in this example for the API. I installed the following packages:

  • Microsoft.AspNetCore.OpenApi
  • Microsoft.AspNetCore.Authentication.JwtBearer
  • Microsoft.Extensions.ApiDescription.Server

The OpenAPI services are added to the project. The API uses JWT Bearer and OAuth and so the OpenAPI definitions should also add this. An BearerSecuritySchemeTransformer implementation was created like in the Microsoft documentation.

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});

The BearerSecuritySchemeTransformer class implements the OpenAPI specific definitions. If using cookies, only the document.Info is required and this can be added directly in the services without an extra class. From the Microsoft docs:

internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
    {
        var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
        if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
        {
            var requirements = new Dictionary<string, OpenApiSecurityScheme>
            {
                ["Bearer"] = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer", // "bearer" refers to the header name here
                    In = ParameterLocation.Header,
                    BearerFormat = "Json Web Token"
                }
            };
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes = requirements;
        }
        document.Info = new()
        {
            Title = "My API Bearer scheme",
            Version = "v1",
            Description = "API for Damien"
        };
    }
}

The middleware needs to be added to the pipeline. You should only add the OpenAPI in development mode unless you require this in production.

app.MapOpenApi("/openapi/v1/openapi.json");

ASP.NET Core supports two types of API implementations; Controller based and minimal APIs. Controller based APIs are used in this project. This API endpoint requires a valid access token and have the different Endpoint definitions.

[Authorize(AuthenticationSchemes = "Bearer")]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController(
	ILogger<WeatherForecastController> _logger) : ControllerBase
{
    [EndpointSummary("This is a summary from OpenApi attributes.")]
    [EndpointDescription("This is a description from OpenApi attributes.")]
    [Produces(typeof(IEnumerable<WeatherForecast>))]
    [HttpGet("GetWeatherForecast")]
    public IActionResult Get()
    {
        _logger.LogDebug("GetWeatherForecast with OpenAPI definitions");

        return Ok(Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray());
    }

You can also add definitions for POSTs and parameters or whatever you require.

[EndpointSummary("This is a second summary from OpenApi attributes.")]
[EndpointDescription("This is a second description from OpenApi attributes.")]
[Produces(typeof(IEnumerable<WeatherForecast>))]
[HttpPost("PostWeatherForecast")]
public IActionResult PostWeatherForecast(
    [Description("parameter post item using OpenApi")] WeatherForecast weatherForecast)
{
    return Ok(weatherForecast);
}

When the application is started, the Json OpenAPI definitions can be downloaded and used.

Notes

I am not sure how good the OpenAPI specifications are and need to validate how to define the different status codes in the specifications with the correct return types.

Links

https://github.com/martincostello/dotnet-minimal-api-integration-testing

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/aspnetcore-openapi

https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types

https://github.com/RicoSuter/NSwag

3 comments

  1. […] Implementing an ASP.NET Core API with .NET 9 and OpenAPI (Damien Bowden) […]

  2. Unknown's avatar

    […] Implementing an ASP.NET Core API with .NET 9 and OpenAPI […]

  3. Lasse's avatar
    Lasse · · Reply

    What if i have a directory structure with separate controllers, how can i generate the OpenApi json with endpoint from all of these controllers at the same url?

Leave a reply to Add a Swagger UI using a .NET 9 Json OpenAPI file | Software Engineering Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.