Is there an existing issue for this?
Describe the bug
When we have a model like this:
public class Order
{
[Required]
public required string OrderNumber { get; set; }
}
and use it in a Controller API like so:
[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
[HttpPost(Name = "Make Order")]
public IActionResult Post(Order order)
{
Console.WriteLine(order.OrderNumber.ToUpper());
return Ok("Order made");
}
}
Then when we call it:
POST {{ControllerAPI_HostAddress}}/Order/
Content-Type: application/json
Accept: application/json
{}
We get back some Problem Details:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"$": [
"JSON deserialization for type 'ControllerAPI.Order' was missing required properties including: 'orderNumber'."
],
"order": [
"The order field is required."
]
},
"traceId": "00-b3e09b166ef942bc0d6e50fc2368e4ef-b69710dd2bd4f14d-00"
}
But when we try to make a similar setup with Minimal API:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidation(); // Using the new validation
var app = builder.Build();
app.MapPost("/order", (Order order) =>
{
Console.WriteLine(order.OrderNumber.ToUpper());
return Results.Ok("Order made");
})
.WithName("Make Order");
app.Run();
And call it with the same payload:
POST {{MinimalAPI_HostAddress}}/Order/
Content-Type: application/json
Accept: application/json
{}
We instead get back:
Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to read parameter "Order order" from the request body as JSON.
---> System.Text.Json.JsonException: JSON deserialization for type 'MinimalAPI.Order' was missing required properties including: 'orderNumber'.
at System.Text.Json.ThrowHelper.ThrowJsonException_JsonRequiredPropertyMissing(JsonTypeInfo parent, BitArray assignedOrNotRequiredPropertiesSet)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize[TReadBufferState,TStream](TReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, T& value)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync[TReadBufferState,TStream](TStream utf8Json, TReadBufferState bufferState, CancellationToken cancellationToken)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(PipeReader utf8Json, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForJson>g__TryReadBodyAsync|102_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest, JsonTypeInfo jsonTypeInfo)
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Http.RequestDelegateFactory.Log.InvalidJsonRequestBody(HttpContext httpContext, String parameterTypeName, String parameterName, Exception exception, Boolean shouldThrow)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForJson>g__TryReadBodyAsync|102_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest, JsonTypeInfo jsonTypeInfo)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass102_2.<<HandleRequestBodyAndCompileRequestDelegateForJson>b__2>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
Expected Behavior
I would also expect to get back Problem Details for Minimal API.
And if we were in a really ideal world, then it would simply return that the OrderNumber field was required for both Controller API and Minimal API:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"OrderNumber": [
"The OrderNumber field is required."
]
},
"traceId": "00-516cb8a80977b468f9cb7a0ba121048f-001d81939c66676a-00"
}
But I understand that it would be technically challenging because the object needs to be deserialized before it can be validated against its DataAnnotations. But I imagine that many will combine the usage of the [Required] attribute and the required keyword because the alternative is not beautiful (it feels dirty to write default!):
public class Order
{
[Required]
public string OrderNumber { get; set; } = default!;
}
Steps To Reproduce
I have made a minimal repo for this here: https://github.com/KristofferStrube/ModelValidationInApiWithRequiredProperty
In the repo, I have also added tests that show that the Minimal API does not return problem details in the specific scenario.
Exceptions (if any)
No response
.NET Version
10.0.100-rc.2.25502.107
Anything else?
No response
Is there an existing issue for this?
Describe the bug
When we have a model like this:
and use it in a Controller API like so:
Then when we call it:
We get back some Problem Details:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "$": [ "JSON deserialization for type 'ControllerAPI.Order' was missing required properties including: 'orderNumber'." ], "order": [ "The order field is required." ] }, "traceId": "00-b3e09b166ef942bc0d6e50fc2368e4ef-b69710dd2bd4f14d-00" }But when we try to make a similar setup with Minimal API:
And call it with the same payload:
We instead get back:
Expected Behavior
I would also expect to get back Problem Details for Minimal API.
And if we were in a really ideal world, then it would simply return that the
OrderNumberfield was required for both Controller API and Minimal API:{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "OrderNumber": [ "The OrderNumber field is required." ] }, "traceId": "00-516cb8a80977b468f9cb7a0ba121048f-001d81939c66676a-00" }But I understand that it would be technically challenging because the object needs to be deserialized before it can be validated against its
DataAnnotations. But I imagine that many will combine the usage of the[Required]attribute and therequiredkeyword because the alternative is not beautiful (it feels dirty to writedefault!):Steps To Reproduce
I have made a minimal repo for this here: https://github.com/KristofferStrube/ModelValidationInApiWithRequiredProperty
In the repo, I have also added tests that show that the Minimal API does not return problem details in the specific scenario.
Exceptions (if any)
No response
.NET Version
10.0.100-rc.2.25502.107
Anything else?
No response