ApiEndpoints: Binding parameters from multiple locations not working in .NET 6?
- NuGet Package Version: 3.1.0
- .NET SDK Version: 6.0-RC-1
I am trying to bind parameters from multiple locations as specified in the documentation, but it does not seem to work.
Endpoint:
[HttpPut("api/projects/{projectId}")]
public override async Task<ActionResult<UpdateProjectResponse>> HandleAsync([FromRoute] UpdateProjectRequest request, CancellationToken cancellationToken = default)
{
}
Request:
public class UpdateProjectRequest
{
[FromRoute(Name = "projectId")] public Guid ProjectId { get; set; } = default!;
[FromBody] public string Code { get; set; } = default!;
[FromBody] public string Description { get; set; } = default!;
[FromBody] public string Number { get; set; } = default!;
}
This is the response I get:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-8204c6f7087a1e60fb4c1ab5ac75c778-de42a5cad4fe62d6-00",
"errors": {
"$": [
"The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1.",
"The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
],
"Code": [
"The Code field is required.",
"'Code' must not be empty."
],
"Number": [
"The Number field is required.",
"'Number' must not be empty."
],
"Description": [
"The Description field is required.",
"'Description' must not be empty."
]
}
}
I am also using FluentValidation, but that should not matter because FluentValidation validates after model binding.
Could this be because something has changed in .NET 6 or am I just missing something?
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 1
- Comments: 18 (7 by maintainers)
What I have done myself as a workaround is creating a BaseAsyncEndpoint myself with multiple WithResponse classes. One with only one parameter, one with two parameters etc.
I have done it like this: https://gist.github.com/luuk777w/5f8d1a7a6f178da4884e87ca7c0b9e38 It works really well, and as a bonus it plays nice with Swagger. However, the downside of this approach is that you will limit the number of parameters by how many classes you define.
@ardalis I have just tested it and the issue remains. I have created a reproduction repo: https://github.com/luuk777w/ApiEndpointsIssueRepro
I am not having exactly the same result as described above, I do however not get the Id from the route (though it is displayed in swagger ui as a required route parameter), but the body also includes the id and this is what is being bound to the command object.
Not sure whether this is a swagger issue or an ApiEndpoint issue.
After finding a previous problem I managed to work around this issue, maybe it’ll help solve the underlying issue.
must include FromRoute in handler:
HandleAsync([FromRoute]FinishRequest request,
then the request can be split into two parts:
The only downside is that referencing the request will need an additional property:
request.Body.EndTime
. I don’t see this as a big deal as it further defines where the data is coming from.As a bonus, Swagger displays and passes the request as expected too, and correctly defined the Body component.