Skip to content

AspNetCoreOpenApiDocumentGenerator

Rico Suter edited this page Oct 5, 2020 · 22 revisions

This generator uses the ASP.NET Core API Explorer service. The AspNetCoreOpenApiDocumentGenerator class is used to generate a OpenAPI/Swagger specification from ASP.NET Core controllers using the API Explorer and is used by the AspNetCore Middleware or via CLI.

Important: If you run this generator via NSwag.MSBuild as part of your .csproj build (after build target), you need to set NoBuild to true to avoid endless build recursions...

Remarks: If you are using .NET Core >= v2.1, you may need to set the CompatibilityVersion.

This generator replaces the reflection based generator WebApiOpenApiDocumentGenerator. It is recommended to always use this generator for ASP.NET Core projects!

Request, response and model schemas

The generator internally uses the JsonSchemaGenerator class from the NJsonSchema project to generate the JSON Schemas of the request and response DTO types. All settings and extension points can also be used in NSwag (see wiki for more information).

OpenAPI related attributes

  • Package: NSwag.Annotations

OpenApiIgnoreAttribute()

Excludes a Web API method from the Swagger specification.

OpenApiOperationAttribute(operationId)

Defines a custom operation ID for a Web API action method.

OpenApiTagsAttribute(tags)

Defines the operation tags. See Specify the operation tags.

OpenApiExtensionDataAttribute()

Adds extension data to the document (when applied to a controller class), an operation or parameter.

OpenApiBodyParameterAttribute([mimeType])

Specifies that the operation consumes the POST body. This is used when the body is read programmatically in the operation and not specified as a method parameter ([FromBody]).

SwaggerResponseAttribute(httpAction, type) (not recommended/deprecated, use ASP.NET Core's ProducesResponseTypeAttribute instead)

Defines the response type of a Web API action method and HTTP action. See Specify the response type of an action.

SwaggerDefaultResponseAttribute() (not recommended/deprecated, use ASP.NET Core's ProducesDefaultResponseTypeAttribute instead)

Adds the default response (HTTP 200/204) based on the return type of the operation method. This can be used in conjunction with the SwaggerResponseAttribute or another response defining attribute (ProducesResponseTypeAttribute, etc.). This is needed because if one of these attributes is available, you have to define all responses and the default response is not automatically added. If an HTTP 200/204 response is already defined then the attribute is ignored (useful if the attribute is defined on the controller or the base class).

  • Package: NJsonSchema

NotNullAttribute and CanBeNullAttribute

Can be defined on DTO properties (handled by NJsonSchema), operation parameters and the return type with:

[return: NotNull]
public string GetName()

The default behavior can be changed with the WebApiOpenApiDocumentGeneratorSettings.DefaultReferenceTypeNullHandling setting (default: Default = Use Nullable Reference Types or Null for reference types).

There are many more attributes to control DTO schema generation.

BindRequiredAttribute (ASP.NET Core)

Requires that an HTTP operation parameter is bound. Makes the parameter not-nullable and required in the specification.

Specify the operation response type and description

In ASP.NET Core you should only use ASP.NET Core attributes and avoid SwaggerResponseAttributes and SwaggerDefaultResponseAttributes.

The response type and description can be defined with the ProducesResponseTypeAttribute and XML docs:

/// <summary>
/// Creates an order.
/// </summary>
/// <param name="order"></param>
/// <response code="201">Order created.</response>
/// <response code="400">Order invalid.</response>
[HttpPost]
[ProducesResponseType(typeof(int), 201)]
[ProducesResponseType(typeof(IDictionary<string, string>), 400)]
public IActionResult CreateOrder()
{
    return new CreatedResult("/orders/1", 1);
}

To define an operation without a response (i.e. HTTP 204 or 404), use the ProducesResponseType attribute and typeof(void):

If the response type cannot be determined, the type of generated response will be file (sample is Swagger 2.0):

"responses": {
	"200": {
		"schema": {
			"type": "file"
		}
	}
}

To force a file response, specify the response type with the ProducesResponseTypeAttribute:

[ProducesResponseType(typeof(IActionResult), 200)]
public IActionResult DownloadFile(string name)
{
    ....
}

Working with HTTP status codes and exceptions

We recommend to catch exceptions and return them with a custom HTTP status code.

[HttpPost]
[ProducesResponseType(typeof(int), 200)]
[ProducesResponseType(typeof(LocationNotFoundException), 500)]
public async Task<ActionResult> Create([FromBody]Person person)
{
    try
    {
        var location = await _geoService.FindLocationAsync(person.Location);
        person.LocationLatitude = location.Latitude;
        person.LocationLongitude = location.Longitude;
    }
    catch (LocationNotFoundException locationNotFoundException)
    {
        return StatusCode(500, locationNotFoundException);
    }

    await _dataContext.SaveChangesAsync();
    return Ok(person.Id);
}

In order to correctly serialize the custom exception, just add the JsonExceptionConverter from the NJsonSchema NuGet package (or use the JsonExceptionFilterAttribute:

[JsonConverter(typeof(JsonExceptionConverter))]
public class LocationNotFoundException : Exception
{
    [JsonProperty("location")]
    public string Location { get;  }

    public LocationNotFoundException(string location, Exception exception)
        : base("The location could not be found.", exception)
    {
        Location = location;
    }
}

Response nullability

By default responses in ASP.NET Core are nullable (i.e. you can return null in your operation). However because most people design their APIs to never return null the default in the spec generator is to make responses not nullable. A not nullable response would look like this (OpenAPI 3):

"/pet/{petId}": {
  "get": {
    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Pet"
            }
          }
        }
      },

You can change this behavior globally by changing the DefaultResponseReferenceTypeNullHandling setting to Null. To specify the nullability of a specific response, use XML docs like this:

/// <response code="200" nullable="true">The order.</response>
[HttpPost]
[ProducesResponseType(typeof(Order), 200)]
public IActionResult GetOrder(int id)
{
    ...
}

A nullable response in OpenAPI 3 would look like this:

"/pet/{petId}": {
  "get": {
    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "nullable": true,
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Pet"
                }
              ]
            }
          }
        }
      },

For more information, have a look at this PR.

.NET Core 3 and C# 8

Support for System.Text.Json

NSwag cannot fully support System.Text.Json because the library does not expose metadata to generate schemas. However we internally try to map System.Text.Json options to a custom Newtonsoft.JSON contract resolver which should cover most scenarios.

See this epic for more information: https://github.com/RicoSuter/NSwag/issues/2243

Nullable Reference Types

NSwag supports handling of Nullable Reference Types:

image

Specify the operation tags

With the OpenApiTagsAttribute you can specify the Swagger/OpenAPI tags for a Web API action method:

[OpenApiTags("foo", "bar")]
public void MyActionMethod() 
{
	...
}

If the attribute is not available, the controller name is added to the list of operation tags.

File uploads

An operation parameter is treated as form file upload if it is/inherits/implements one of the following types (or a collection of it):

  • IFormFile
  • HttpPostedFile
  • HttpPostedFileBase

You can also add the OpenApiFileAttribute to a parameter or a class (implemented in the NSwag.Annotations package):

public void MyOperation([OpenApiFile] MyClass myParameter)
{
    ...
}

Known issues