| File: ModelBinding\Binders\EnumTypeModelBinder.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.Globalization; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders; /// <summary> /// <see cref="IModelBinder"/> implementation to bind models for types deriving from <see cref="Enum"/>. /// </summary> public class EnumTypeModelBinder : SimpleTypeModelBinder { /// <summary> /// Initializes a new instance of <see cref="EnumTypeModelBinder"/>. /// </summary> /// <param name="suppressBindingUndefinedValueToEnumType"> /// Flag to determine if binding to undefined should be suppressed or not. /// </param> /// <param name="modelType">The model type.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>,</param> /// <remarks> /// The <paramref name="suppressBindingUndefinedValueToEnumType"/> parameter is currently ignored. /// </remarks> public EnumTypeModelBinder( bool suppressBindingUndefinedValueToEnumType, Type modelType, ILoggerFactory loggerFactory) : base(modelType, loggerFactory) { } /// <inheritdoc/> protected override void CheckModel( ModelBindingContext bindingContext, ValueProviderResult valueProviderResult, object? model) { if (model == null) { base.CheckModel(bindingContext, valueProviderResult, model); } else if (IsDefinedInEnum(model, bindingContext)) { bindingContext.Result = ModelBindingResult.Success(model); } else { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueIsInvalidAccessor( valueProviderResult.ToString())); } } private static bool IsDefinedInEnum(object model, ModelBindingContext bindingContext) { var modelType = bindingContext.ModelMetadata.UnderlyingOrModelType; // Check if the converted value is indeed defined on the enum as EnumTypeConverter // converts value to the backing type (ex: integer) and does not check if the value is defined on the enum. if (bindingContext.ModelMetadata.IsFlagsEnum) { // Enum.IsDefined does not work with combined flag enum values. // From EnumDataTypeAttribute.cs in CoreFX. // Examples: // // [Flags] // enum FlagsEnum { Value1 = 1, Value2 = 2, Value4 = 4 } // // Valid Scenarios: // 1. valueProviderResult="Value2,Value4", model=Value2 | Value4, underlying=6, converted=Value2, Value4 // 2. valueProviderResult="2,4", model=Value2 | Value4, underlying=6, converted=Value2, Value4 // // Invalid Scenarios: // 1. valueProviderResult="2,10", model=12, underlying=12, converted=12 // var underlying = Convert.ChangeType( model, Enum.GetUnderlyingType(modelType), CultureInfo.InvariantCulture).ToString(); var converted = model.ToString(); return !string.Equals(underlying, converted, StringComparison.OrdinalIgnoreCase); } return Enum.IsDefined(modelType, model); } }