domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.22k stars 1.3k forks source link

Non-nullable string in record shows as nullable in swagger output #2674

Closed pferngren closed 1 year ago

pferngren commented 1 year ago

I'm trying to correctly specify a string property in a record as being not nullable, but everything I try results in the property being taged as nullable in the generated swagger.

To reproduce the issue create a new .net7 web api with OpenAPI support .csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.7" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
  </ItemGroup>

</Project>

No need to change anything in the program.cs from the template Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Add a controller and a record model ExampleController.cs

using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;

namespace NullableStringExample.Controllers;

[Route("api/[controller]")]
[ApiController]
public class ExampleController : ControllerBase
{
    [HttpGet("record", Name = nameof(GetRecord))]
    [ProducesResponseType(typeof(ExampleModel), StatusCodes.Status200OK)]
    public IActionResult GetRecord()
    {
        return Ok(new ExampleModel("NonNullable", "Nullable", 1, 1));
    }
}

public record ExampleModel(
    [Required] string NonNullableString,
    string? NullableString,
    int NonNullableInt,
    int? NullableInt
    );

The generated swagger:

{
  "openapi": "3.0.1",
  "info": {
    "title": "NullableStringExample",
    "version": "1.0"
  },
  "paths": {
    "/api/Example/record": {
      "get": {
        "tags": [
          "Example"
        ],
        "operationId": "GetRecord",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExampleModel"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExampleModel"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExampleModel"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ExampleModel": {
        "type": "object",
        "properties": {
          "nonNullableString": {
            "type": "string",
            "nullable": true // <--- This should not be there
          },
          "nullableString": {
            "type": "string",
            "nullable": true
          },
          "nonNullableInt": {
            "type": "integer",
            "format": "int32"
          },
          "nullableInt": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}

Adding the required attribute works for classes but not for records

pferngren commented 1 year ago

Finally found the fix for this issue, you have to opt in to support for nullable reference types in the setup for swagger gen

services.AddSwaggerGen(options =>
{
    options.SupportNonNullableReferenceTypes();
});
hashproton commented 5 months ago

Finally found the fix for this issue, you have to opt in to support for nullable reference types in the setup for swagger gen

services.AddSwaggerGen(options =>
{
    options.SupportNonNullableReferenceTypes();
});

Thank you, it helped a lot.