You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

显示效果

显示效果:模态框外逗号分隔,框内点分隔


问题根源分析

  1. 输入控件本地化不一致:<InputNumber>默认使用浏览器的文化设置(English(UK)用点作为小数分隔符),而应用显示逻辑使用的是hr-HR文化(逗号作为小数分隔符),导致显示与输入的分隔符不统一。
  2. API参数绑定错误:API服务器默认使用的文化(大概率为en-US)将URL中的10,0000解析为千分符格式,直接转换为100000,而非预期的10.0000。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:42:03