Open jacobjmarks opened 2 years ago
Hi @jacobjmarks, I realize it's been over a year since this was posted, but the issue is still open, and we ran into something similar.
We were able to handle both [FromBody]
and [FromForm]
for a single route. We used a combination of SwaggerGenOptions.ResolveConflictingActions()
and a new attribute that implemented IActionConstraint
.
First, as you already have, you need two separate functions in your controller class, with the same route and HTTP method, but different function names. One has a [FromBody]
parameter and the other has [FromForm]
.
Second, put all your Consumes
content types on the FromBody
function. Remove the Consumes
attribute from the FromForm
function. Having multiple content types causes Swashbuckle to create requestBody.content for each content type.
"requestBody": {
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/MyForm"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/MyForm"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/MyForm"
}
}
}
},
Third, handle the conflicting actions detected by Swashbuckle. Because we put all of the Consumes
attributes on one function, we can use that as our filter to find the correct action to use.
.AddSwaggerGen(options =>
{
options.ResolveConflictingActions(apiDescriptions =>
apiDescriptions.First(d => d.ActionDescriptor.ActionConstraints.Any(ac => ac is ConsumesAttribute)));
})
In our code, we have additional checks, such as only handling a certain .RelativePath
because we only needed this for backwards compatibility of our API, and we don't want to create new endpoints following this pattern. You may need to experiment and find what works for you.
Lastly, we need to get asp.net MVC to use the FromForm function if we receive form data. For that we created an IActionConstraint
attribute:
public class FormContentTypeAttribute : Attribute, IActionConstraint
{
public int Order => 0;
public bool Accept(ActionConstraintContext ctx) =>
ctx.RouteContext.HttpContext.Request.HasFormContentType;
}
This says the function handles a request with form content, and Order = 0 means it takes precedent over any other function with the same route and HTTP method. This effectively gets .net to ignore [Consumes("multipart/form-data")]
it sees on the FromBody function.
The two functions look something like this:
[Route("form")]
[HttpPost]
[Consumes("application/x-www-form-urlencoded", "application/json", "multipart/form-data")]
public IActionResult PostFromBody([FromBody] MyForm form) => Ok(form);
[Route("form")]
[HttpPost]
[FormContentType]
public IActionResult PostFromForm([FromForm] MyForm form) => Ok(form);
Credit for the IActionConstraint implementation from Stack Overflow: https://stackoverflow.com/questions/51673361/how-to-combine-frombody-and-fromform-bindingsource-in-asp-net-core
Hello.
I currently face the same issue, however, I cannot apply the mentioned workaround as I use different request schemas for the different content types. Is there any ETA on when this will be fixed or does somebody know about a workaround that can solve this issue?
This issue is stale because it has been open for 60 days with no activity. It will be automatically closed in 14 days if no further updates are made.
I am currently attempting to utilise the following Controller, based on Microsoft's own documentation to support a single HTTP endpoint that can digest both Form and Body requests.
Functionally, this works perfectly. Requests are routed to either Action method based on the incoming
Content-Type
.The Swagger specification for this endpoint, I would expect to be defined simply as follows:
However, during schema generation, a
Conflicting method/path combination
error is encountered;This issue has been previously discussed within #1798 however I feel the proposed workarounds - either "taking the first definition" during specification generation (producing an inaccurate spec) or splitting the single endpoint into two (modifying functionality) - are not suitable.
Consumes
attribute?SwaggerGenOptions.ResolveConflictingActions()
extension method to achieve my desired schema which accurately represents all supported content types?~ .NET SDK:
6.0.100
Swashbuckle.AspNetCore:6.2.3