将空格字符绑定到char属性的模型绑定

4

我有一个简单的视图模型,其中包含一个char属性...

public char Character1 { get; set; }

默认的模型绑定似乎无法将空格字符(" ")转换为该属性,导致以下ModelState错误...
The Character1 field is required.

HTML输入元素是在JavaScript中创建的:

var input = $('<input type="password" name="Character' + i + '" id="input-' + i + '" data-val="true" data-val-custom maxlength="1"></input>');
  • 该属性上没有[Required]属性。
  • 被提交的值在模型错误的AttemptedValue属性中明确为空格。
  • ModelState.IsValid由于上述错误返回false。
  • 绑定后,模型属性具有空字符值\0

为什么空格字符不能绑定到char属性?

更新:

char属性更改为string可以正常绑定。


空格是否返回给控制器? - user1666620
你能展示视图中相关的代码吗? - user1666620
更新了带有标记示例的问题。 - Brett Postin
我认为这是因为文本框固有地存储了字符串类型的值,而不是字符类型的值。 - Ahmed ilyas
不幸的是,这并不能解释为什么普通字符按预期绑定到char属性... - Brett Postin
显示剩余2条评论
4个回答

2

我认为这是DefaultModelBinder的一个缺陷。如果你在你的操作中使用FormCollection,字符串会以空格返回。

这个IModelBinder实现展示了默认模型绑定器的行为,并提供了一种可能的解决方案:

public class CharModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var dmb = new DefaultModelBinder();
        var result = dmb.BindModel(controllerContext, bindingContext);
        // ^^ result == null

        var rawValueAsChar = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(char));
        // ^^ rawValueAsChar == null

        var rawValueAsString = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
        if(!string.IsNullOrEmpty(rawValueAsString))
            return rawValueAsString.ToCharArray()[0];
        return null;
    }
}

在您的 Global.asax 中注册它:

ModelBinders.Binders.Add(typeof(char), new CharModelBinder());

2
原因很简单,char被定义为值类型(struct),而string被定义为引用类型(class)。这意味着char是非空的,并且必须有一个值。
这就是为什么DefaultModelBinder(您可能正在使用)会自动将此属性的验证元数据设置为required,即使您没有添加[Required]属性。
这是ModelMetaData.cs(第58行)的源代码
_isRequired = !TypeHelpers.TypeAllowsNullValue(modelType);

因此,您最终的结果是将Character1属性的ModelMetaData.Required设置为true

但是,您可以显式配置DataAnnotationsModelValidatorProvider不将值类型自动设置为required,使用以下内容:

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

请见MSDN

谢谢,但为什么空格字符不被视为有效值?我认为“ ”与空字符串或null是不同的。我目前正在检查源代码,看看为什么会有所不同的处理方式。 - Brett Postin

1

最近在.NET Core中遇到了这个问题,因为SimpleTypeModelBinder也有相同的检查,所以添加了以下内容:

    using System;
using Microsoft.AspNetCore.Mvc.ModelBinding;
public class CharModelBinderProvider : IModelBinderProvider { /// public IModelBinder GetBinder( ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.Metadata.ModelType == typeof(char)) { return new CharModelBinder(); }
return null; } }
using System; using System.ComponentModel; using System.Runtime.ExceptionServices; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
/// /// /// An for char. /// /// /// Difference here is that we allow for a space as a character which the does not. /// public class CharModelBinder : IModelBinder { private readonly TypeConverter _charConverter;
public CharModelBinder() { this._charConverter = new CharConverter(); }
/// public Task BindModelAsync( ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); }
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) { // no entry return Task.CompletedTask; }
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
try { var value = valueProviderResult.FirstValue; var model = this._charConverter.ConvertFrom(null, valueProviderResult.Culture, value); this.CheckModel(bindingContext, valueProviderResult, model);
return Task.CompletedTask; } 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);
// Were able to find a converter for the type but conversion failed. return Task.CompletedTask; } }
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); } } }

在Startup中:

serviceCollection.AddMvc(options => { options.ModelBinderProviders.Insert(0, new CharModelBinderProvider()); })


(注:该代码为C#语言)

1

好的,我已经在 System.Web.Mvc.ValueProviderResult 中找到了有问题的代码:

private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType)
    {
      if (value == null || destinationType.IsInstanceOfType(value))
        return value;
      string str = value as string;
      if (str != null && string.IsNullOrWhiteSpace(str))
        return (object) null;
      ...
}

我不确定这是否是一个错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接