包含货币值的输入字段的客户端验证错误

问题描述 投票:0回答:2

我正在使用Asp.Net Core 2.1和Code First开发一个Web应用程序。我有一堆十进制类型的属性,并使用以下属性进行装饰:

        [DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]

问题是当表单进入编辑模式时,客户端验证会抛出以下错误,因为输入字段包含货币符号:

该字段必须是数字。

如何告诉asp.net核心将货币符号作为十进制值处理输入字段?

c# asp.net-core code-first
2个回答
0
投票

尝试创建一个custom model binder,它将(比如说)“$ 15,481”转换回小数。

您要查找的结果是将输入验证为货币而不是小数,但在进行模型绑定之前,您可能还需要擦除输入,因此您将使用描述Scrub操作的界面。

 public interface IScrubberAttribute
{
    object Scrub(string modelValue, out bool success);
}

接下来,添加CurrencyScrubberAttribute,它将执行解析用户输入的工作,以查看它是否是有效的货币格式。 C#的decimal.TryParse有一个超载,需要一个NumberStyleCultureInfo,这是如何进行货币验证。你会发现这只适用于美国货币($),但只需要设置CultureInfo来处理其他货币。

[AttributeUsage(AttributeTargets.Property)]
public class CurrencyScrubberAttribute : Attribute, IScrubberAttribute
{
    private static NumberStyles _currencyStyle = NumberStyles.Currency;
    private CultureInfo _culture = new CultureInfo("en-US");

    public object Scrub(string modelValue, out bool success)
    {
        var modelDecimal = 0M;
        success = decimal.TryParse(
            modelValue,
            _currencyStyle,
            _culture,
            out modelDecimal
        );
        return modelDecimal;
    }
}

使用如下的新CurrencyScrubberAttribute

 public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    [DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
    [CurrencyScrubber]
    public decimal Price { get; set; }
}

添加模型绑定器。对于像Product这样的强类型模型,ComplexTypeModelBinderProvider接受挑战,然后为每个属性创建一个binder。

 public class ScrubbingModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        if (!context.Metadata.IsComplexType&&context.Metadata.PropertyName!=null)
        {
            // Look for scrubber attributes
            var propName = context.Metadata.PropertyName;
            var propInfo = context.Metadata.ContainerType.GetProperty(propName);

            // Only one scrubber attribute can be applied to each property
            var attribute = propInfo.GetCustomAttributes(typeof(IScrubberAttribute), false).FirstOrDefault();
            if (attribute != null)
                return new ScrubbingModelBinder(context.Metadata.ModelType, attribute as IScrubberAttribute);
        }
        return null;
    }
}

模型绑定器将处理具有IScrubberAttribute的简单类型,但如果由于任何原因我们不打算处理绑定,我们将把它传递给SimpleTypeModelBinder来处理它。

public class ScrubbingModelBinder : IModelBinder
{
    IScrubberAttribute _attribute;
    SimpleTypeModelBinder _baseBinder;

    public ScrubbingModelBinder(Type type, IScrubberAttribute attribute)
    {
        if (type == null) throw new ArgumentNullException(nameof(type));

        _attribute = attribute as IScrubberAttribute;
        _baseBinder = new SimpleTypeModelBinder(type);
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));

        // Check the value sent in
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult != ValueProviderResult.None)
        {
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            // Attempt to scrub the input value
            var valueAsString = valueProviderResult.FirstValue;
            var success = true;
            var result = _attribute.Scrub(valueAsString, out success);
            if (success)
            {
                bindingContext.Result = ModelBindingResult.Success(result);
                return Task.CompletedTask;
            }
        }
        // If we haven't handled it, then we'll let the base SimpleTypeModelBinder handle it
        return _baseBinder.BindModelAsync(bindingContext);
    }
}

在ConfigureServices中添加它

services.AddMvc(options =>
        {
            options.ModelBinderProviders.Insert(0, new ScrubbingModelBinderProvider());
        });

-1
投票

货币文本可以显示在单独的位置,数据可以显示在文本框中。

这是相同的代码。

在模型中

 public class Test
{
        [Key]
        [MaxLength(30)]
        public string Id { get; set; }

        public string Name { get; set; }

        [DataType(DataType.Currency)]
        [DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
        public float? Cost { get; set; }


}

在视图中

@model CoreCodeFist.Models.Dataobj.Test
@{
    ViewData["Title"] = "Home Page";
}
<div class="row">    
    <div class="col-md-3">
        <form asp-action="Index">
            <h2>Test</h2>
            <div class="input-group">
                <span class="input-group-addon">@string.Format("{0:C}", Model.Cost!=null?Model.Cost:0).FirstOrDefault()</span>
                <input asp-for="Cost" asp-format="{0}" class="form-control" />
            </div>
            <div class="input-group">
                <span asp-validation-for="Cost" class="text-danger"></span>
            </div>
            <br />
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
            </form>
</div>

</div>

在控制器中

//First Page Load Method In Initialize Model Because Cost Is Initialize
        public IActionResult Index()
        {

            return View(new Test());
        }
        //This Is Edit Method 
        public IActionResult Edit(float id)
        {
            Test t = new Test()
            {
                Cost = id
            };
            return View("Index",t);
        }
        //First Page Post Method
        [HttpPost]
        public IActionResult Index(Test model)
        {
            return View(model);
        }
© www.soinside.com 2019 - 2024. All rights reserved.