.NET 8 Blazor WASM InputNumber本地化数值提交转换异常求助
Blazor WebAssembly decimal输入本地化转换异常问题解决
我在.NET 8 Blazor WebAssembly项目中使用<EditForm>和decimal类型输入字段时遇到数值转换异常:变量中存储的是10,00,Blazor将其转换为10.0000(末尾零不影响),但通过API提交表单后,该值被保存为100000。
已知问题源于本地化配置,已尝试多种方案(包括BingChat、Github Copilot及Microsoft官方文档)均未解决。当前浏览器设置为English(UK),应用需强制使用hr-HR(克罗地亚语)本地化,且不受浏览器设置影响。
补充信息:问题中的<InputNumber>位于Bootstrap模态框内,模态框下方显示的数值使用逗号(,)作为小数分隔符,而模态框内的<InputNumber>输入框使用点(.)作为小数分隔符。
相关代码
1. InputNumber组件代码
<InputNumber TValue="decimal" @bind-Value="@item.Quantity" class="form-control form-control-lg" />
2. 模态框完整代码
<div class="col-sm-5 col-md-5 rounded-3 border-danger border fs-3 text-center" role="button" data-bs-toggle="modal" data-bs-target="#ProducedQuantity-@item.FactoryOrderItemId"> @item.Quantity.RoundToString() </div> <div class="clearix"></div> ////////////////////////////// /// #ProducedQuantity-@orderItemId ////////////////////////////// <div class="modal fade" id="ProducedQuantity-@item.FactoryOrderItemId" tabindex="-1" aria-labelledby="ProducedQuantityLabel-@item.FactoryOrderItemId" aria-hidden="true"> <div class="modal-dialog modal-dialog-scrollable"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="ProducedQuantityLabel-@item.FactoryOrderItemId"> @Frontend.ProducedQuantity </h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <div class="row"> <div class="col-sm-20 col-md-20 align-content-center text-center"> @item.ProductName @if (!string.IsNullOrWhiteSpace(item.ConnectedName)) { <span class="text-warning"><i class="bi bi-flower3"></i></span> <span>@item.ConnectedName</span> } </div> <div class="col-sm-20 col-md-20"> <br /> </div> <div class=" col-sm-10 col-md-10"> <div class="input-group input-group-lg"> <button class="btn btn-lg btn-outline-primary" type="button" @onclick="(() => FactoryOrderItemQuantityMinus(item))"><i class="bi bi-dash-lg"></i></button> <InputNumber TValue="decimal" @bind-Value="@item.Quantity" class="form-control form-control-lg" /> <button class="btn btn-lg btn-outline-primary" type="button" @onclick="(() => FactoryOrderItemQuantityPlus(item))"><i class="bi bi-plus-lg"></i></button> </div> </div> <div class="col-md-9 col-sm-9 align-content-center text-center"> <button type="button" @onclick="(() => UpdateProducedQuantity(item))" class="btn btn-outline-primary btn-lg" data-bs-dismiss="modal"> @Frontend.Save </button> </div> </div> </div> </div> </div> </div> <div class="clearfix"></div>
3. 页面保存函数
private async Task UpdateProducedQuantity(FactoryOrderItemListVM item) { await _factory.UpdateProducedQuantity(item.FactoryOrderItemId, item.Quantity); StateHasChanged(); }
4. WASM API请求服务
public async Task UpdateProducedQuantity(int FactoryOrderItemId, decimal Quantity) { if(FactoryOrderItemId > 0) { var response = await _httpClient.PutAsync($"api/Factory/UpdateProducedQuantity/{FactoryOrderItemId}/{Quantity}", null); if (response.IsSuccessStatusCode) { _sys.ToastSuccess(); return; } else { await _sys.ToastErrorAsync(response); return; } } _sys.ToastError(Errors.ParameterNotValid); return; }
5. 发送的API请求示例
https://localhost:7226/api/Factory/UpdateProducedQuantity/50/10,0000
6. API控制器代码
[HttpPut("UpdateProducedQuantity/{FactoryOrderItemId}/{Quantity}")] public async Task<ActionResult> UpdateProducedQuantity(int FactoryOrderItemId, decimal Quantity) { try { if (FactoryOrderItemId > 0) { await _factory.UpdateProducedQuantityAsync(FactoryOrderItemId, Quantity); _logger.LogDebug("UpdateProducedQuantity: {@FactoryOrderItemId} {@Quantity}", FactoryOrderItemId, Quantity); return Ok(); } return BadRequest(Errors.ParameterNotValid); } catch (Exception ex) { _logger.LogDebug("Exception: {@ex}", ex); return StatusCode(500, Errors.FactoryUpdateProducedQuantity); } }
7. Decimal显示扩展方法
public static string RoundToString(this decimal Val) { decimal roundedValue = Math.Round(Val, 2, MidpointRounding.AwayFromZero); return roundedValue.ToString("0.00"); }
显示效果

问题根源分析
- 输入控件本地化不一致:
<InputNumber>默认使用浏览器的文化设置(English(UK)用点作为小数分隔符),而应用显示逻辑使用的是hr-HR文化(逗号作为小数分隔符),导致显示与输入的分隔符不统一。 - API参数绑定错误:API服务器默认使用的文化(大概率为en-US)将URL中的
10,0000解析为千分符格式,直接转换为100000,而非预期的10.0000。 - URL传递decimal的风险:将decimal值放在URL路径中,容易因文化差异导致解析错误,且URL对特殊字符的处理存在不确定性。
解决方案
1. 强制Blazor应用使用hr-HR文化,不受浏览器影响
在WASM项目的Program.cs中配置全局本地化:
using System.Globalization; var builder = WebAssemblyHostBuilder.CreateDefault(args); // 注册本地化服务,指定默认文化为hr-HR,禁用浏览器文化探测 builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); var supportedCultures = new[] { new CultureInfo("hr-HR") }; builder.Services.Configure<RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture("hr-HR"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; // 禁用从浏览器请求中探测文化 options.RequestCultureProviders.Clear(); }); // 自定义InputNumber组件,强制使用hr-HR解析输入 builder.Services.AddScoped<InputNumber<decimal>, LocalizedInputNumber>(); public class LocalizedInputNumber : InputNumber<decimal> { protected override bool TryParseValueFromString(string? value, out decimal result, out string validationErrorMessage) { validationErrorMessage = string.Empty; return decimal.TryParse(value, NumberStyles.Number, new CultureInfo("hr-HR"), out result); } }
2. 修复API端参数绑定问题
方案A:修改API全局文化配置
在API项目的Program.cs中设置全局文化为hr-HR,确保参数绑定使用正确的小数分隔符:
using System.Globalization; var builder = WebApplication.CreateBuilder(args); // 设置全局线程文化为hr-HR CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("hr-HR"); CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("hr-HR"); // 配置JSON序列化使用hr-HR文化 builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Culture = new CultureInfo("hr-HR"); });
方案B:改用请求体传递参数(推荐)
避免在URL路径中传递decimal,改用请求体传递彻底规避文化解析问题:
- 修改WASM服务代码:
public async Task UpdateProducedQuantity(int FactoryOrderItemId, decimal Quantity) { if(FactoryOrderItemId > 0) { var requestDto = new UpdateQuantityDto { FactoryOrderItemId = FactoryOrderItemId, Quantity = Quantity }; var jsonContent = JsonContent.Create(requestDto); var response = await _httpClient.PutAsync($"api/Factory/UpdateProducedQuantity", jsonContent); if (response.IsSuccessStatusCode) { _sys.ToastSuccess(); return; } await _sys.ToastErrorAsync(response); return; } _sys.ToastError(Errors.ParameterNotValid); } // 定义请求DTO public class UpdateQuantityDto { public int FactoryOrderItemId { get; set; } public decimal Quantity { get; set; } }
- 修改API控制器:
[HttpPut("UpdateProducedQuantity")] public async Task<ActionResult> UpdateProducedQuantity([FromBody] UpdateQuantityDto request) { try { if (request.FactoryOrderItemId > 0) { await _factory.UpdateProducedQuantityAsync(request.FactoryOrderItemId, request.Quantity); _logger.LogDebug("UpdateProducedQuantity: {@FactoryOrderItemId} {@Quantity}", request.FactoryOrderItemId, request.Quantity); return Ok(); } return BadRequest(Errors.ParameterNotValid); } catch (Exception ex) { _logger.LogDebug("Exception: {@ex}", ex); return StatusCode(500, Errors.FactoryUpdateProducedQuantity); } }
3. 统一显示逻辑的文化
修改扩展方法,指定使用hr-HR文化确保显示逗号分隔:
public static string RoundToString(this decimal Val) { decimal roundedValue = Math.Round(Val, 2, MidpointRounding.AwayFromZero); // 指定hr-HR文化格式化数值 return roundedValue.ToString("0.00", new CultureInfo("hr-HR")); }
内容的提问来源于stack exchange,提问作者HerGiz
相关产品推荐
相关产品推荐

