You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When deserializing Json to DTOs in MVC model binding, for non-nullable value type members null Json values are rejected, a nice error message is added to ModelState and a 400 error returned. However, for non-nullable reference types null values are let through which can then trip ArgumentNullExceptions in object constructors or property setters leading to a more cryptic 500 error rather than producing a 400 and a friendlier ModelState error.
An option could be added to (probably) JsonSerializerOptions so that it respects non-nullable reference types and throws a JsonException when encountering a null value, rather than binding the null value.
Motivation and goals
Current work around is to always use nullable reference types for all reference type members in DTOs, and defer null checks until after the constructor in validation or business logic. The DTO is unable to defend its not-null invariants itself, leading to pollution of the codebase with null checks elsewhere. Effectively this makes using nullable reference types not usable/useful for controller action DTOs.
An option could be added to (probably) JsonSerializerOptions so that it respects non-nullable reference types and throws a JsonException when encountering a null value, rather than binding the null value.
This may be more aptly posted in the runtime repo as presumably the changes need to be in System.Text.Json, but as it is impacting me in my controllers I thought it would make sense to get feedback here first.
Examples
This small console app demonstrates the issue:
namespaceJsonNullable{staticclassProgram{publicrecordNullableInt(int?someNumber);publicrecordNonNullableInt(intsomeNumber);publicrecordNullableString(string?SomeString);publicrecordNonNullableString{publicstringSomeString{get;}publicNonNullableString(stringsomeString)=>SomeString=someString??thrownew ArgumentNullException(nameof(someString));}staticvoidMain(string[]args){varnullableInt=new NullableInt(null);varjsonInt= JsonSerializer.Serialize(nullableInt);varnonNullableInt= JsonSerializer.Deserialize<NonNullableInt>(jsonInt);// ^^ throws Cannot get the value of a token type 'Null' as a number.// Gets handled gracefully by MVC model bindingvarnullableString=new NullableString(null);varjsonString= JsonSerializer.Serialize(nullableString);varnonNullableString= JsonSerializer.Deserialize<NonNullableString>(jsonString);// ^^ throws ArgumentNullException from constructor// Causes 500 exception in MVC, forcing the null check to be removed from the DTO and performed after binding}}}
The text was updated successfully, but these errors were encountered:
Summary
When deserializing Json to DTOs in MVC model binding, for non-nullable value type members null Json values are rejected, a nice error message is added to ModelState and a 400 error returned. However, for non-nullable reference types null values are let through which can then trip ArgumentNullExceptions in object constructors or property setters leading to a more cryptic 500 error rather than producing a 400 and a friendlier ModelState error.
An option could be added to (probably) JsonSerializerOptions so that it respects non-nullable reference types and throws a JsonException when encountering a null value, rather than binding the null value.
Motivation and goals
Current work around is to always use nullable reference types for all reference type members in DTOs, and defer null checks until after the constructor in validation or business logic. The DTO is unable to defend its not-null invariants itself, leading to pollution of the codebase with null checks elsewhere. Effectively this makes using nullable reference types not usable/useful for controller action DTOs.
An option could be added to (probably) JsonSerializerOptions so that it respects non-nullable reference types and throws a JsonException when encountering a null value, rather than binding the null value.
e.g. in Startup.cs
Examples
This small console app demonstrates the issue:
The text was updated successfully, but these errors were encountered: