RicoSuter / NSwag

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

Neither OpenApiBodyParameter nor Consumes are respected with FromForm #4048

Open hauntingEcho opened 2 years ago

hauntingEcho commented 2 years ago

I'm using the MSBuild & CLI option to generate a static OpenAPI file as part of my build pipeline. For an OAuth2 token endpoint, I need to support an application/x-www-form-urlencoded POST body (while the rest of my application uses application/json).

My endpoint signature:

        [AllowAnonymous]
        [Consumes("application/x-www-form-urlencoded")]
        [OpenApiBodyParameter("application/x-www-form-urlencoded")] // workaround attempt per https://github.com/RicoSuter/NSwag/issues/2508
        [HttpPost]
        [Route("/v2/oauth2/token")]
        [ValidateModelState]
        [SwaggerResponse(StatusCodes.Status200OK, typeof(string))]
        [SwaggerResponse(StatusCodes.Status401Unauthorized, null)]
        public IActionResult RequestToken([FromForm(Name = "grant_type")] OAuth2GrantType grant_type)

The generated requestBody:

      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                grant_type:
                  $ref: '#/components/schemas/OAuth2GrantType'
          application/x-www-form-urlencoded:
            schema:
              type: string
              format: binary

The expected requestBody:

      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              properties:
                grant_type:
                  $ref: '#/components/schemas/OAuth2GrantType'

Additional info:

maglini commented 1 year ago

Is there any update?

mhx8 commented 3 weeks ago

@hauntingEcho @maglini

You can try to implement a custom IOperationProcessor

public class OctetStreamProcessor(string parameterName) : IOperationProcessor
{
    private const string ApplicationOctetStream = "application/octet-stream";
    private const string Format = "byte";

    public bool Process(OperationProcessorContext context)
    {
        ArgumentNullException.ThrowIfNull(context);
        context.OperationDescription.Operation.RequestBody = new OpenApiRequestBody
        {
            Name = parameterName,
            Content =
            {
                [ApplicationOctetStream] = new OpenApiMediaType
                {
                    Schema = new JsonSchema
                    {
                        Type = JsonObjectType.String,
                        Format = Format
                    }
                }
            }
        };

        return true;
    }
}

And then use it in your Controller Method:

[OpenApiOperationProcessor(typeof(OctetStreamProcessor), "saveDocumentInput")]
public async Task<IActionResult> Save([FromBody] byte[] saveDocumentInput, [FromQuery] Guid id)
{
// impl...

Then you dont need ConsumeAttributeor OpenApiBodyParameterAttribute