Skip to content

Honor [Consumes] / IAcceptsMetadata on form endpoints#2589

Merged
jeremydmiller merged 2 commits intoJasperFx:mainfrom
rmasciarella:feat/form-endpoints-honor-consumes
Apr 26, 2026
Merged

Honor [Consumes] / IAcceptsMetadata on form endpoints#2589
jeremydmiller merged 2 commits intoJasperFx:mainfrom
rmasciarella:feat/form-endpoints-honor-consumes

Conversation

@rmasciarella
Copy link
Copy Markdown
Contributor

Summary

HttpChain.fillRequestType only honors [Consumes] / IAcceptsMetadata when the endpoint has a body request type and is not a form endpoint (HasRequestType && !IsFormData). Form endpoints fall through and apiDescription.SupportedRequestFormats stays empty.

ASP.NET Core 10's OpenApiDocumentService.GetFormRequestBody then defaults to application/x-www-form-urlencoded:

if (supportedRequestFormats.Count == 0)
{
    supportedRequestFormats = [new ApiRequestFormat { MediaType = "application/x-www-form-urlencoded" }];
}

This silently drops [Consumes("multipart/form-data")] on file-upload endpoints, so generated clients (Orval, NSwag, Kiota) emit URLSearchParams bodies instead of FormData — file uploads break at runtime.

Fix

Extend CreateApiDescription so that for endpoints with form-source parameters and no formats already populated, IAcceptsMetadata flows into SupportedRequestFormats:

if (apiDescription.SupportedRequestFormats.Count == 0 &&
    apiDescription.ParameterDescriptions.Any(p =>
        p.Source == BindingSource.Form || p.Source == BindingSource.FormFile))
{
    foreach (var metadata in Endpoint!.Metadata.OfType<IAcceptsMetadata>())
    {
        foreach (var contentType in metadata.ContentTypes)
        {
            apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat
            {
                MediaType = contentType
            });
        }
    }
}

Semantics

  • No [Consumes] → no change. Wolverine still emits zero formats; MS OpenAPI still defaults to application/x-www-form-urlencoded.
  • [Consumes("multipart/form-data")] → multipart is added to SupportedRequestFormats; MS OpenAPI no longer adds the urlencoded default (its count == 0 guard); spec correctly emits multipart.
  • Non-form body endpoints → unaffected (the form-parameter guard short-circuits).

Regression test

New test endpoint:

[WolverinePost("/form/multipart-consumes")]
[Consumes("multipart/form-data")]
public static string MultipartConsumes([FromForm] string value) => value ?? "";

New test:

[Fact]
public void form_endpoints_honor_consumes_metadata_for_supported_request_formats()
{
    var chain = HttpChains.ChainFor("POST", "/form/multipart-consumes");
    var apiDescription = chain!.CreateApiDescription("POST");

    apiDescription.SupportedRequestFormats
        .Select(f => f.MediaType)
        .ShouldContain("multipart/form-data");
}

Fails on main:

but was actually
Failed!  - Failed: 1, Passed: 0

Passes after the fix:

Passed!  - Failed: 0, Passed: 1

Test plan

  • New test fails on main
  • New test passes after fix
  • Form + OpenAPI / Swashbuckle filtered run: 64 passed, 0 failed, 8 pre-existing skips

Related

Independent of #2587 (which fixes a separate ParameterDescriptor null-deref in the same file). Either can land first.

Copilot AI review requested due to automatic review settings April 26, 2026 09:54
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes OpenAPI request-format discovery for Wolverine form endpoints so that [Consumes] / IAcceptsMetadata is reflected in ApiDescription.SupportedRequestFormats, preventing ASP.NET Core OpenAPI from silently defaulting form endpoints to application/x-www-form-urlencoded and breaking generated multipart clients.

Changes:

  • Seed SupportedRequestFormats from IAcceptsMetadata for form endpoints when no formats are already present.
  • Add a regression endpoint that declares [Consumes("multipart/form-data")] on a form-bound parameter.
  • Add a regression test asserting multipart is present in SupportedRequestFormats for the new endpoint.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Http/WolverineWebApi/Forms/FormEndpoints.cs Adds a form endpoint decorated with [Consumes("multipart/form-data")] to reproduce the issue.
src/Http/Wolverine.Http/HttpChain.ApiDescription.cs Extends CreateApiDescription to flow IAcceptsMetadata into SupportedRequestFormats for form endpoints.
src/Http/Wolverine.Http.Tests/using_form_parameters.cs Adds a regression test ensuring form endpoints honor consumes metadata in SupportedRequestFormats.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 172 to 184
foreach (var metadata in Endpoint!.Metadata.OfType<IAcceptsMetadata>())
{
foreach (var contentType in metadata.ContentTypes)
{
apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat
{
MediaType = contentType
});
}
}
}

return apiDescription;
Comment on lines +161 to +165
// fillRequestType only honors [Consumes] / IAcceptsMetadata for body
// endpoints (HasRequestType && !IsFormData). Without this, form
// endpoints fall through and ASP.NET Core OpenAPI's GetFormRequestBody
// defaults SupportedRequestFormats to "application/x-www-form-urlencoded"
// — silently dropping [Consumes("multipart/form-data")] on file-upload
Comment on lines +544 to +549
// IAcceptsMetadata when the endpoint has a body request type and is
// not a form endpoint. Form endpoints used to fall through and rely
// on ASP.NET Core OpenAPI's "application/x-www-form-urlencoded"
// default, which silently dropped [Consumes("multipart/form-data")]
// and caused generated clients (Orval, NSwag) to emit URLSearchParams
// bodies instead of multipart for file-upload endpoints.
@jeremydmiller
Copy link
Copy Markdown
Member

@rmasciarella Thank you for taking this on. OpenAPI is about my least favorite part of working on Wolverine.

Copy link
Copy Markdown
Member

@jeremydmiller jeremydmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the form-endpoint OpenAPI fix is correct and the regression test demonstrates the bug clearly. Will follow up with the Copilot review's dedup recommendation in a separate PR.

@jeremydmiller jeremydmiller merged commit 95db99e into JasperFx:main Apr 26, 2026
18 of 21 checks passed
@rmasciarella
Copy link
Copy Markdown
Contributor Author

@jeremydmiller no worries it just came up for me while vibe coding, i did run it through a few different models from different angles to make sure before i put it up, glad that it was helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants