| File: ModelBinding\Binders\SimpleTypeModelBinder.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System.ComponentModel; using System.Runtime.ExceptionServices; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders; /// <summary> /// An <see cref="IModelBinder"/> for simple types. /// </summary> public class SimpleTypeModelBinder : IModelBinder { private readonly TypeConverter _typeConverter; private readonly ILogger _logger; /// <summary> /// Initializes a new instance of <see cref="SimpleTypeModelBinder"/>. /// </summary> /// <param name="type">The type to create binder for.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> public SimpleTypeModelBinder(Type type, ILoggerFactory loggerFactory) { ArgumentNullException.ThrowIfNull(type); ArgumentNullException.ThrowIfNull(loggerFactory); _typeConverter = TypeDescriptor.GetConverter(type); _logger = loggerFactory.CreateLogger(typeof(SimpleTypeModelBinder)); } /// <inheritdoc /> public Task BindModelAsync(ModelBindingContext bindingContext) { ArgumentNullException.ThrowIfNull(bindingContext); _logger.AttemptingToBindModel(bindingContext); var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) { _logger.FoundNoValueInRequest(bindingContext); // no entry _logger.DoneAttemptingToBindModel(bindingContext); return Task.CompletedTask; } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { var value = bindingContext.ModelMetadata.IsFlagsEnum ? valueProviderResult.Values.ToString() : valueProviderResult.FirstValue; object? model; if (bindingContext.ModelType == typeof(string)) { // Already have a string. No further conversion required but handle ConvertEmptyStringToNull. if (bindingContext.ModelMetadata.ConvertEmptyStringToNull && string.IsNullOrWhiteSpace(value)) { model = null; } else { model = value; } } else if (string.IsNullOrWhiteSpace(value)) { // Other than the StringConverter, converters Trim() the value then throw if the result is empty. model = null; } else { model = _typeConverter.ConvertFrom( context: null, culture: valueProviderResult.Culture, value: value); } CheckModel(bindingContext, valueProviderResult, model); } catch (Exception exception) { var isFormatException = exception is FormatException; if (!isFormatException && exception.InnerException != null) { // TypeConverter throws System.Exception wrapping the FormatException, // so we capture the inner exception. exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException; } bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, exception, bindingContext.ModelMetadata); } _logger.DoneAttemptingToBindModel(bindingContext); return Task.CompletedTask; } /// <summary> /// If the <paramref name="model" /> is <see langword="null" />, verifies that it is allowed to be <see langword="null" />, /// otherwise notifies the <see cref="P:ModelBindingContext.ModelState" /> about the invalid <paramref name="valueProviderResult" />. /// Sets the <see cref="P:ModelBindingContext.Result" /> to the <paramref name="model" /> if successful. /// </summary> protected virtual void CheckModel( ModelBindingContext bindingContext, ValueProviderResult valueProviderResult, object? model) { // When converting newModel a null value may indicate a failed conversion for an otherwise required // model (can't set a ValueType to null). This detects if a null model value is acceptable given the // current bindingContext. If not, an error is logged. if (model == null && !bindingContext.ModelMetadata.IsReferenceOrNullableType) { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor( valueProviderResult.ToString())); } else { bindingContext.Result = ModelBindingResult.Success(model); } } }