Workleap.Extensions.OpenAPI 0.3.4-preview.10

Prefix Reserved
This is a prerelease version of Workleap.Extensions.OpenAPI.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Workleap.Extensions.OpenAPI --version 0.3.4-preview.10                
NuGet\Install-Package Workleap.Extensions.OpenAPI -Version 0.3.4-preview.10                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Workleap.Extensions.OpenAPI" Version="0.3.4-preview.10" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Workleap.Extensions.OpenAPI --version 0.3.4-preview.10                
#r "nuget: Workleap.Extensions.OpenAPI, 0.3.4-preview.10"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Workleap.Extensions.OpenAPI as a Cake Addin
#addin nuget:?package=Workleap.Extensions.OpenAPI&version=0.3.4-preview.10&prerelease

// Install Workleap.Extensions.OpenAPI as a Cake Tool
#tool nuget:?package=Workleap.Extensions.OpenAPI&version=0.3.4-preview.10&prerelease                

Workleap.Extensions.OpenAPI

nuget build

The Workleap.Extensions.OpenAPI library is designed to help generate better OpenApi document with less effort.

Value proposition and features overview

The library offers an opinionated configuration of OpenAPI document generation and SwaggerUI.

As such, we provide the following features:

OpenAPI Spec generation filters:

  • Display OperationId in SwaggerUI
  • Extract Type schema from TypedResult endpoint response types.
  • Ensure that non-nullable properties are marked as required in the OpenAPI document. It is no longer necessary to add [Required] attributes to object properties.
  • (Optional) Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint.

Roslyn Analyzers to help validate usage typed responses:

  • Rule to catch mismatches between endpoint response annotations and Typed Responses
  • Rule to help enforce usage of strongly typed responses

Getting started

Install the package Workleap.Extensions.OpenAPI in your .NET API project. Then you may use the following method to register the required service. Here is a code snippet on how to register this and to enable the operationId fallback feature in your application.

public void ConfigureServices(IServiceCollection services)
{
  // [...]
  services.ConfigureOpenApiGeneration()
    .GenerateMissingOperationId(); // Optional
}

We support the extraction of Response Types automatically. For example, considering the following API code snippet:

[HttpGet]
[Route("/get-example/{id}")]
public Results<Ok<TypedResultExample>, BadRequest<ProblemDetails>, NotFound> GetExample(int id)
{
    return id switch
    {
        < 0 => TypedResults.NotFound(),
        0 => TypedResults.BadRequest(new ProblemDetails()),
        _ => TypedResults.Ok(new TypedResultExample("Example"))
    };
}

The resulting OpenAPI snippet would be generated:

  /get-example:
    get:
      tags:
        - TypedResult
      operationId: TypedResultWithNoAnnotation2
      parameters:
        - name: id
          in: query
          style: form
          schema:
            type: integer
            format: int32
      responses:
        '200':
          description: '200'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TypedResultExample'
        '400':
          description: '400'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '404':
          description: '404'

One thing to note is that the library does not overwrite any explicitly defined ResponseType attributes on the endpoint. For example, considering the endpoints below, despite having ProblemDetails being defined as the TypedResult return, the schema will defer to the TypedResultExample schema given that it is explicitly defined in the SwaggerResponse or ProducesResponseType attributes.

[HttpGet]
[Route("/withSwaggerResponseAnnotation")]
[SwaggerResponse(StatusCodes.Status200OK, "Returns TypedResult", typeof(TypedResultExample), "application/json")] 
// The OpenAPI document would be generated with the TypedResultExample schema rather than ProblemDetails as per signature. 
public Ok<ProblemDetails> TypedResultWithSwaggerResponseAnnotation()
{
    return TypedResults.Ok(new ProblemDetails());
}

[HttpGet]
[Route("/producesResponseTypeAnnotation")]
[ProducesResponseType(typeof(TypedResultExample), StatusCodes.Status200OK)] 
// The OpenAPI document would be generated with the TypedResultExample schema rather than ProblemDetails as per signature.
public Ok<ProblemDetails> TypedResultWithProducesResponseTypeAnnotation()
{
    return TypedResults.Ok(new ProblemDetails());
}

Note: TypedResults requires Microsoft.AspNetCore.Http.Json.JsonOptions to be configured

TypedResults uses obtains the JsonOptions from Microsoft.AspNetCore.Http.Json.JsonOptions. If you are using IActionResult return types, typically, you would configure Microsoft.AspNetCore.Mvc.JsonOptions which also configure the JsonSerializerOptions for SwashBuckle. To use this library, you need to configure both JsonOptions. Here is a snippet of code demonstrating that:

// API Extension methods

// SwashBuckle and TypedResults require different JsonOptions to be configured.
// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2293
public static IServiceCollection ConfigureJsonSerializerOptions(this IServiceCollection services)
{
    services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options => ConfigureJsonOptions(options.SerializerOptions));
    services.Configure<Microsoft.AspNetCore.Mvc.JsonOptions>(options => ConfigureJsonOptions(options.JsonSerializerOptions));

    return services;
}

private static void ConfigureJsonOptions(JsonSerializerOptions options)
{
    options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    options.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
    options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    options.Converters.Add(new JsonStringEnumConverter());
}

Included Roslyn analyzers

Rule ID Category Severity Description
WLOAS001 Design Warning Mismatch between annotation return type and endpoint return type.
WLOAS002 Usage Warning Enforce strongly typed endpoint response.

To modify the severity of one of these diagnostic rules, you can use a .editorconfig file. For example:

## Disable analyzer for test files
[**Tests*/**.cs]
dotnet_diagnostic.WLOAS001.severity = none
dotnet_diagnostic.WLOAS002.severity = none

To learn more about configuring or suppressing code analysis warnings, refer to this documentation.

WLOAS001: Mismatch between annotation return type and endpoint return type.

This rule validates the return type indicated by the endpoint annotations against the Typed response values indicated by the endpoint. Here is an example:

[HttpGet]
[Route("/example")]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)] // This would be marked with a warning given typeof(string) is different from typeof(TypedResultExample)
[ProducesResponseType(typeof(TypedResultExample), StatusCodes.Status200OK)] // This would be valid
public Ok<TypedResultExample> TypedResultExample()
{
    return TypedResults.Ok(new TypedResultExample());
}

WLOAS002: Enforce strongly typed endpoint response.

This rule enforces the usage of strongly type responses for endpoints. The usage of weakly response types such as IActionResult and IResult would be marked with warnings. Here is an example of a warning:

[HttpGet]
[Route("/example")]
public IActionResult EnforceStronglyTypedResponse() //This is not a strongly typed response and would be marked with a warning
{
    return TypedResults.Ok(new TypedResultExample());
}

Here is an example of a strongly typed response:

[HttpGet]
[Route("/example")]
public Ok<TypedResultExample> EnforceStronglyTypedResponse() //This is a strongly typed response 
{
    return TypedResults.Ok(new TypedResultExample());
}

Limitations

Given that HttpResults return types do not leverage the configured Formatters, content negotiation is not supported for these endpoints and the produced Content-Type is decided by HttpResults implementation. For our use cases, it will be application/json.

Building, releasing and versioning

The project can be built by running Build.ps1. It uses Microsoft.CodeAnalysis.PublicApiAnalyzers to help detect public API breaking changes. Use the built-in roslyn analyzer to ensure that public APIs are declared in PublicAPI.Shipped.txt, and obsolete public APIs in PublicAPI.Unshipped.txt.

A new preview NuGet package is automatically published on any new commit on the main branch and whenever we execute the CI pipeline.

When you are ready to officially release a stable NuGet package by following the SemVer guidelines, simply manually create a tag with the format x.y.z. This will automatically create and publish a NuGet package for this version.

License

Copyright © 2024, Workleap This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license at License.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.4.1-preview.3 39 12/18/2024
0.4.1-preview.2 47 12/18/2024
0.4.1-preview.1 40 12/17/2024
0.4.0 171 12/10/2024
0.3.4-preview.18 42 11/29/2024
0.3.4-preview.17 43 10/30/2024
0.3.4-preview.16 42 10/28/2024
0.3.4-preview.15 42 10/28/2024
0.3.4-preview.14 40 10/28/2024
0.3.4-preview.13 53 9/30/2024
0.3.4-preview.12 48 9/26/2024
0.3.4-preview.11 38 9/20/2024
0.3.4-preview.10 35 9/20/2024
0.3.4-preview.9 56 9/13/2024
0.3.4-preview.8 56 9/13/2024
0.3.4-preview.7 55 9/11/2024
0.3.4-preview.6 52 8/28/2024
0.3.4-preview.5 49 8/27/2024
0.3.4-preview.4 49 8/27/2024
0.3.4-preview.3 53 7/15/2024
0.3.4-preview.2 47 7/15/2024
0.3.4-preview.1 40 7/5/2024
0.3.3 9,187 6/13/2024
0.3.3-preview.1 54 6/13/2024
0.3.2 176 6/12/2024
0.3.2-preview.1 51 6/12/2024
0.3.1-preview.2 58 6/4/2024
0.3.1-preview.1 66 5/31/2024
0.3.0 165 5/22/2024
0.2.1-preview.3 67 5/22/2024
0.2.1-preview.2 58 5/21/2024
0.2.1-preview.1 56 5/21/2024
0.2.0 473 5/13/2024
0.1.1-preview.4 231 5/13/2024
0.1.1-preview.3 64 5/10/2024
0.1.1-preview.2 73 5/10/2024
0.1.1-preview.1 67 5/9/2024