Skip to content

Commit 752b539

Browse files
committed
Allowing empty input
1 parent f7ccefb commit 752b539

File tree

7 files changed

+43
-60
lines changed

7 files changed

+43
-60
lines changed

src/Mvc/Mvc.Abstractions/src/ModelBinding/BindingInfo.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,15 @@ public bool TryApplyBindingInfo(ModelMetadata modelMetadata)
246246
PropertyFilterProvider = modelMetadata.PropertyFilterProvider;
247247
}
248248

249-
if (EmptyBodyBehavior == EmptyBodyBehavior.Default && modelMetadata.IsEmptyBodyAllowed != null)
249+
// If the EmptyBody behavior is not configured will be infer
250+
// as Allow when the ModelMetadata.IsRequired is false or HasDefaultValue
251+
// https://github.com/dotnet/aspnetcore/issues/39754
252+
if (EmptyBodyBehavior == EmptyBodyBehavior.Default &&
253+
BindingSource == BindingSource.Body &&
254+
(!modelMetadata.IsRequired || modelMetadata.HasDefaultValue))
250255
{
251256
isBindingInfoPresent = true;
252-
EmptyBodyBehavior = modelMetadata.IsEmptyBodyAllowed == true ? EmptyBodyBehavior.Allow : EmptyBodyBehavior.Disallow;
257+
EmptyBodyBehavior = EmptyBodyBehavior.Allow;
253258
}
254259

255260
return isBindingInfoPresent;

src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,10 @@ internal IReadOnlyDictionary<ModelMetadata, ModelMetadata> BoundConstructorPrope
500500
internal virtual string? ValidationModelName { get; }
501501

502502
/// <summary>
503-
/// Gets or sets the value which decides if empty bodies are treated as valid inputs.
503+
/// Gets the value that indicates if the parameter has a default value set.
504+
/// This is only available when <see cref="MetadataKind"/> is <see cref="ModelMetadataKind.Parameter"/> otherwise it will be false.
504505
/// </summary>
505-
internal virtual bool? IsEmptyBodyAllowed { get; set; }
506+
internal bool HasDefaultValue { get; private set; }
506507

507508
/// <summary>
508509
/// Throws if the ModelMetadata is for a record type with validation on properties.
@@ -615,6 +616,7 @@ private void InitializeTypeInformation()
615616
IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null;
616617
IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType;
617618
UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType;
619+
HasDefaultValue = MetadataKind == ModelMetadataKind.Parameter && Identity.ParameterInfo!.HasDefaultValue;
618620

619621
var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>));
620622
IsCollectionType = collectionType != null;

src/Mvc/Mvc.Core/src/ApplicationModels/InferParameterBindingInfoConvention.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ internal void InferParameterBindingSources(ActionModel action)
8888
message += Environment.NewLine + parameters;
8989
throw new InvalidOperationException(message);
9090
}
91+
else if (fromBodyParameters.Count == 1 &&
92+
fromBodyParameters[0].BindingInfo!.EmptyBodyBehavior == EmptyBodyBehavior.Default &&
93+
IsOptionalParameter(fromBodyParameters[0]))
94+
{
95+
fromBodyParameters[0].BindingInfo!.EmptyBodyBehavior = EmptyBodyBehavior.Allow;
96+
}
9197
}
9298

9399
// Internal for unit testing.
@@ -132,4 +138,30 @@ private bool IsComplexTypeParameter(ParameterModel parameter)
132138

133139
return metadata.IsComplexType;
134140
}
141+
142+
private bool IsOptionalParameter(ParameterModel parameter)
143+
{
144+
if (parameter.ParameterInfo.HasDefaultValue)
145+
{
146+
return true;
147+
}
148+
149+
if (_modelMetadataProvider is ModelMetadataProvider modelMetadataProvider)
150+
{
151+
var metadata = modelMetadataProvider.GetMetadataForParameter(parameter.ParameterInfo);
152+
return !metadata.IsRequired;
153+
}
154+
else
155+
{
156+
// Cannot be determine if the parameter is optional since the provider
157+
// does not provides an option to getMetadata from the parameter info
158+
// so, we will use the Nullability context
159+
160+
// Waiting for PR https://github.com/dotnet/aspnetcore/pull/39804
161+
// No need for information from attributes on the parameter. Just use its type.
162+
// var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterInfo.ParameterType);
163+
// return metadata.NullabilityState != NullabilityState.NotNull;
164+
return false;
165+
}
166+
}
135167
}

src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ internal static void ConfigureAdditionalModelMetadataDetailsProviders(IList<IMet
123123
modelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
124124
modelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
125125

126-
modelMetadataDetailsProviders.Add(new NullableEmptyBodyBindingMetadataProvider());
127-
128126
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special));
129127
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile));
130128
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile));

src/Mvc/Mvc.Core/src/ModelBinding/Metadata/DefaultModelMetadata.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,9 +474,6 @@ public override bool? HasValidators
474474
/// <inheritdoc />
475475
internal override string? ValidationModelName => ValidationMetadata.ValidationModelName;
476476

477-
/// <inheritdoc />
478-
internal override bool? IsEmptyBodyAllowed => BindingMetadata.IsEmptyBodyAllowed;
479-
480477
internal static bool CalculateHasValidators(HashSet<DefaultModelMetadata> visited, ModelMetadata metadata)
481478
{
482479
RuntimeHelpers.EnsureSufficientExecutionStack();

src/Mvc/Mvc.Core/src/ModelBinding/Metadata/NullableEmptyBodyBindingMetadataProvider.cs

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
#nullable enable
2-
Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.NullableEmptyBodyBindingMetadataProvider
3-
Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.NullableEmptyBodyBindingMetadataProvider.CreateBindingMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.BindingMetadataProviderContext! context) -> void
4-
Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.NullableEmptyBodyBindingMetadataProvider.NullableEmptyBodyBindingMetadataProvider() -> void
52
Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider
63
Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void
74
Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void

0 commit comments

Comments
 (0)