Closed jezzsantos closed 6 months ago
Since both [AsParameters]
and the System.Text.Json
deserializer both act upon whether a property is using the required
modifier, AND both throw (albeit different) exceptions if the data is not present, AND neither behavior can be overridden or disabled, we are left with really only two known options to move forward:
required
modifier in all Request DTOs - which we can do with Roslyn rules.[AsParameters]
and default Body binding that deal more effectively with the required
modifier, and then map the thrown exception to a HTTP 400 - Validation
exception. Reference #31 I believe we favour the first option (first) to minimize the amount of customization we are making to the runtime, since these things may be fixed in subsequent versions of ASPNET
A typical Reequest DTO might look like this:
Notice the use of the
required
keyword, since this project (and all projects) have<Nullable>enable</Nullable>
.This works at the C# code level, but these DTO objects are populated by ASPNET at runtime due to minimal API registrations like this:
The problem comes when we submit either of these requests in any HTTP client tool, like this, with an invalid or incomplete request:
Which is clearly missing the required properties like:
Make
,Model
andYear
, then we should end up with aHTTP - 400 Validation
, not anHTTP - 500
!But, instead, we get one of these
HTTP - 500
responses, because APNET (internally) struggles to handle therequired
keyword when there is no data for that property.In fact two different exceptions, depending on a couple of things:
This exception is from a
GET
request that we map to use the[AsParameters]
on the request object, where the required property is missing from the URL:see also: https://github.com/dotnet/aspnetcore/issues/52881#issuecomment-1871456841
This exception is from
POST
request, where the required property is missing from the body of the request :see also this: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/required-properties as to why this is being honored
Solution
We need to resolve this, one way or the other, since an inbound HTTP may be missing any data, and we want the appropriate HTTP response in all these cases:
GET /resource/{Id}
when theId
property is null/missing from the request ->HTTP 404 NotFound
(since no effective route can be matched)GET /resource/{Id}
when another property (i.e.Make
) is missing from the request ->HTTP 400 BadRequest
(since the validator kicks in)POST /resource/{Id}
when theId
property is null/missing from the request ->HTTP 404 NotFound
(since no effective route can be matched)POST /resource/{Id}
when another property (i.e.Make
) is missing from the request ->HTTP 400 BadRequest
(since the validator kicks in)At present, the only workable solution is this:
required
keyword in all Request DTOs, and turn off nullability (i.e.<nullable>disabled</nullable>
or use#pragma CS8618
, either for each class or for the whole assembly).GET
requests, we would also need to make all parameters in the request DTO bestring?
to bypass the issues with the[AsParameters]
exception (sinceGET
requests do not support bodies).