RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.69k stars 1.24k forks source link

Required form parameters are not generated properly in OpenAPI 3.0 and C# client #4379

Open ddaspit opened 1 year ago

ddaspit commented 1 year ago

Parameters in a controller action that are marked with the attributes [FromForm] and [BindRequired] are not properly generated in OpenAPI 3.0. The parameters should be added to the required section of the body schema. This works with Swagger 2.0.

In addition, even if the parameters are marked correctly as required in OpenAPI 3.0, the C# client is not generated correctly when generateOptionalParameters is set to true. Required form parameters are treated as optional and assigned a default value.

Steps to reproduce:

Setup NSwag to use OpenAPI 3.0 in Startup.cs:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddOpenApiDocument(document =>
        {
            document.SchemaType = SchemaType.OpenApi3;
            document.DocumentName = "test";
            document.Title = "testing";
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();
        app.UseOpenApi();
        app.UseSwaggerUi3();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Create a controller class with a required form parameter:

[ApiController]
[Route("api/[controller]")]
public class FormController : Controller
{
    [HttpPost("Action")]
    public ActionResult Action([FromForm, BindRequired] string test)
    {
        return Ok();
    }
}

The generated OpenAPI spec is:

"paths": {
  "/Action": {
    "post": {
      "operationId": "Form_Action",
      "requestBody": {
        "content": {
          "multipart/form-data": {
            "schema": {
              "type": "object",
              "properties": {
                "test": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      }
    }
  }

The expected spec is:

"paths": {
  "/Action": {
    "post": {
      "operationId": "Form_Action",
      "requestBody": {
        "content": {
          "multipart/form-data": {
            "schema": {
              "type": "object",
              "required": [
                "test"
              ],
              "properties": {
                "test": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      }
    }
  }

If you use the expected spec and generate a C# client using generateOptionalParameters, the generated code is:

public virtual async System.Threading.Tasks.Task Action(string test = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
{
    ...
}

The expected code is:

public virtual async System.Threading.Tasks.Task Action(string test, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
{
    ...
}
senchden commented 10 months ago

I can confirm this issue persists in version 13.20.0 (latest stable as of now) and would like to see it fixed.

miroslavgrozdanovski commented 8 months ago

I just encountered the same issue. It's a big deal, isn't it? You are entirely blocked by using NSwag when you have optional fields using [FromForm].