23

I'm using ASP.NET Core 3 and Swashbuckle with mostly default configuration and I have a DTO parameter with a string on it that I want to be non-nullable. How can I achieve this? Note, Required and nullability are separate concerns in Swagger.

It's also using C#8 and the non-nullable stuff, so the compiler should be annotating the property as non-nullable already. It's understandable that Swashbuckle hasn't been updated to take that into account (and maybe can't) but I would like to be able to override the generated metadata somehow.

class MyDto {
    [Required]
    // I want this to show as non-nullable in the swagger documentation (and ideally also be non-nullable in the binding)
    public string TestProp { get; set; }
}

[HttpPost]
public void Post([FromBody] MyDto requestModel) {
}

I have tried making it Required. I also tried adding the Newtonsoft annotations, but none of these seemed to do it.

Relevant bit of Swagger doc that is generated:

    "MyDto": {
      "required": [
        "testProp"
      ],
      "type": "object",
      "properties": {
        "testProp": {
          "type": "string",
          "nullable": true
        }
      },
      "additionalProperties": false
     }

Note that having a string parameter directly as a parameter doesn't generate the nullable attribute. E.g.

[HttpPost("testPost")]
public void Post([FromBody] [Required] string testProp) {
}

will generate

"/api/test/testPost": {
  "post": {
    "tags": [
      "Test"
    ],
    "requestBody": {
      "content": {
        "application/json": {
          "schema": {
            "type": "string"
          }
        },
        "text/json": {
          "schema": {
            "type": "string"
          }
        },
        "application/*+json": {
          "schema": {
            "type": "string"
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "Success"
      }
    }
  }
},
Pang
  • 9,564
  • 146
  • 81
  • 122
Quails4Eva
  • 431
  • 1
  • 5
  • 12
  • Swagger is probably not (yet) looking for the `NullableContextAttribute`. – Lasse V. Karlsen Oct 09 '19 at 08:57
  • [This Swashbuckle issue](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1218) shows that currently optional strings *don't* have the `nullable:true` attribute and end up *non*-nullable. The fix will be released in v5 which is still an RC. Which version did you use? And what does the Swagger document look like? Does it actually include `nullable:true` for `TestProp`? – Panagiotis Kanavos Oct 09 '19 at 09:09
  • @PanagiotisKanavos I'm using v5-rc4. It does include nullable:true for strings (which is what I don't want as I'm trying to specify that null isn't valid for this particular string) – Quails4Eva Oct 09 '19 at 09:18
  • @Quails4Eva post the document in the question itself. It looks like the problem was caused by the fix to the linked issue. Even if you simply commented on that issue, the maintainer would want to see the generated `swagger.json`. It would also make it easier for people to understand what you ask – Panagiotis Kanavos Oct 09 '19 at 09:33
  • 1
    Have you tried using `[JsonProperty(Required = Required.DisallowNull)]` ? – Panagiotis Kanavos Oct 09 '19 at 09:36
  • @PanagiotisKanavos I have tried the JsonProperty attribute, but I think it doesn't work because the project is using the new Json serialiser rather than Json.Net (I'm not 100% sure that's the reason). I've added the generated docs to the question – Quails4Eva Oct 09 '19 at 09:54
  • @Quails4Eva you didn't add the results of using `JsonProperty`. Imagine *you* were posting a new issue - the maintainer doesn't have the time to create a test project. You need to show what the actual problem is and how to reproduce it. In any case SwashBuckle *does* use the Json.NET attributes even if the rest of the project doesn't. Check [this test class](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/Types/JsonPropertyAnnotatedType.cs). Which btw explicitly tests for a string with `DisallowNull` – Panagiotis Kanavos Oct 09 '19 at 09:58
  • @PanagiotisKanavos Sorry, it is now working with that attribute. I'm sure I tried it, but maybe I didn't refresh the page correctly or something, that's my bad. Adding the attribute you suggested to TestProp solves the issue. If you want to add that as an answer I will accept it – Quails4Eva Oct 09 '19 at 10:05

3 Answers3

16

You can now use nullable reference types to mark strings as optional or required:

  • Enable nullable reference types for your project/solution

  • Mark strings in Dtos as nullable:

    public class ExampleDto
    {
       [StringLength(64, MinimumLength = 4)]
       public string RequiredString { get; set; }
    
       [StringLength(200)]
       public string? OptionalString { get; set; }
    }
    
  • Enable Swagger support in your Startup.cs

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

This leads to:

{
  "Example": {
    "type": "object",
    "properties": {
      "requiredString": {
        "maxLength": 64,
        "minLength": 4,
        "type": "string"
      },
      "optionalString": {
        "maxLength": 200,
        "minLength": 0,
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  }
}
PeterB
  • 886
  • 10
  • 18
11

Until v4.01 nullable:true was emitted for optional strings. This broke in the first 5.0 RC versions and nullable:true wasn't emitted at all for optional strings. That, obviously, is wrong.

Starting with 5.0 RC3 optional strings are nullable once again.

To specify that an optional string is not nullable, you need to add [JsonProperty(Required = Required.DisallowNull)] to the property. Copying from one of Swashbuckle's unit tests, this :

    [JsonProperty(Required = Required.DisallowNull)]
    public string StringWithRequiredDisallowNull { get; set; }

Should set the property's Nullable flag :

Assert.False(schema.Properties["StringWithRequiredDisallowNull"].Nullable);

And emit nullable:true.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
1

In 2023 with Swashbuckle.AspNetCore 6.5.0 which is outputting OpenApi 3.0 - you just need to add the [Required] attribute to your response DTO (which is what OP was already doing).

Matt Frear
  • 52,283
  • 12
  • 78
  • 86