【问题标题】:Client Validation Error With Input Fields Containing Currency Values包含货币值的输入字段的客户端验证错误
【发布时间】:2019-02-26 10:23:22
【问题描述】:

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

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

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

字段必须是数字。

如何告诉 asp.net core 将带有货币符号的输入字段视为十进制值?

【问题讨论】:

  • 谢谢,但链接中指出的解决方案并不方便,因为您必须在应用程序中包含货币值的所有输入中添加“asp-format”标签。我正在寻找一个在类中的属性上方设置格式属性的方法,并让 asp.net-core 负责在整个应用程序中以正确的方式显示值。
  • 如果你检查 cmets,他们正在讨论这个问题,你不能仅仅通过添加 DisplayFormat 来真正解决这个问题,他们建议使用 bootsrap 输入组,将货币符号从输入中移出.或者使用一些可以处理它的 JavaScript 插件。

标签: c# asp.net-core code-first


【解决方案1】:

尝试创建一个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 接受挑战,然后为每个属性创建一个绑定器。

 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());
        });

【讨论】:

  • Asp.Net 论坛中同一主题的答案副本!无论如何,这听起来很复杂,我无法让它工作。
【解决方案2】:

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

这是相同的代码。

在模型中

 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);
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-17
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    相关资源
    最近更新 更多