如何在ASP.NET Core MVC+EF Core应用中输入非整数价格?
产品价格输入验证与绑定问题解决方案
问题描述
我开发了一款产品管理应用,产品列表页的每个产品都配有CRUD按钮。输入整数价格时一切正常,但输入带逗号的价格会触发验证错误:
Product Price field must be a number.
输入带点的价格时,价格值总是被设置为0。此前已在Stack Overflow及其他网站查找过资料。
模型代码
[Table("Products")] public class Product { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long? ProductID { get; set; } [DisplayName("Name")] [Required(ErrorMessage = "Name of product is required.")] public string ProductName { get; set; } = string.Empty; [DisplayName("Price")] [Required(ErrorMessage = "Price of product is required.")] [Range(0.01, double.MaxValue, ErrorMessage = "Enter a positive price")] [DataType(DataType.Currency)] [DisplayFormat(DataFormatString = "{0:N}", ApplyFormatInEditMode = true)] [Column(TypeName = "decimal(8,2)")] public decimal ProductPrice { get; set; } }
视图代码
<form asp-controller="Product" asp-action="CreateProduct" method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="@Model.ProductName" class="control-label"></label> <input asp-for="@Model.ProductName" class="form-control" /> <span asp-validation-for="@Model.ProductName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="@Model.ProductPrice" class="control-label"></label> <input asp-for="@Model.ProductPrice" class="form-control" /> <span asp-validation-for="@Model.ProductPrice" class="text-danger"></span> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="submit" class="btn btn-primary">Create</button> </div> </form>
问题原因
这是区域性数字格式不匹配导致的:ASP.NET Core默认可能使用英文区域性(en-US),该区域性用点.作为小数分隔符;而输入的逗号,是部分欧洲区域性(如de-DE、fr-FR)的小数分隔符,导致模型绑定和验证失败。输入点但值变成0,是因为当前请求的区域性无法识别点作为分隔符,绑定错误后默认赋值0。
解决方案
1. 配置全局区域性支持
在Program.cs中设置应用支持多区域性,并允许自动检测请求区域性:
var builder = WebApplication.CreateBuilder(args); // 配置请求本地化选项 builder.Services.Configure<RequestLocalizationOptions>(options => { var supportedCultures = new[] { new CultureInfo("en-US"), new CultureInfo("de-DE"), new CultureInfo("fr-FR"), // 添加需要支持的其他区域性 }; options.DefaultRequestCulture = new RequestCulture("en-US"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; // 设置区域性检测优先级 options.RequestCultureProviders = new List<IRequestCultureProvider> { new QueryStringRequestCultureProvider(), new CookieRequestCultureProvider(), new AcceptLanguageHeaderRequestCultureProvider() }; }); var app = builder.Build(); // 启用请求本地化中间件(需放在路由中间件之前) app.UseRequestLocalization(); // 其他中间件配置... app.Run();
2. 修正模型Range特性类型不匹配
将Range特性中的double.MaxValue替换为decimal.MaxValue,避免decimal与double类型转换带来的问题:
[DisplayName("Price")] [Required(ErrorMessage = "Price of product is required.")] [Range(0.01m, decimal.MaxValue, ErrorMessage = "Enter a positive price")] [DataType(DataType.Currency)] [DisplayFormat(DataFormatString = "{0:N}", ApplyFormatInEditMode = true)] [Column(TypeName = "decimal(8,2)")] public decimal ProductPrice { get; set; }
3. 前端输入格式化优化(可选)
添加JavaScript代码,根据浏览器当前语言自动格式化价格输入,提升用户体验:
const priceInput = document.getElementById('ProductPrice'); priceInput.addEventListener('blur', function() { const rawValue = this.value.replace(/[^0-9.,]/g, ''); // 统一转换为数字 const number = parseFloat(rawValue.replace(',', '.')); if (!isNaN(number)) { // 根据浏览器语言格式化 this.value = number.toLocaleString(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } });
4. 强制动作使用特定区域性(可选)
如果不需要多区域性支持,可在控制器动作上强制指定区域性,比如支持逗号分隔的德语区域性:
[HttpPost] [Culture("de-DE")] public IActionResult CreateProduct(Product model) { if (ModelState.IsValid) { // 执行产品保存逻辑 return RedirectToAction(nameof(Index)); } return View(model); }
内容的提问来源于stack exchange,提问作者TA1989
相关产品推荐
相关产品推荐

