ASP.NET Core MVC:前端格式化货币字符串绑定转Decimal至控制器
解决方案
可以通过自定义模型绑定器或者类型转换器实现格式字符串到decimal?的转换,且确保转换在验证前执行,以下是两种可行方案:
方案一:自定义模型绑定器
自定义绑定器可直接处理表单提交的字符串,先清理格式再转换为decimal?,步骤如下:
- 创建自定义模型绑定器类:
public class FormattedDecimalBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); // 获取表单提交的原始值 var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) return Task.CompletedTask; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); var rawValue = valueProviderResult.FirstValue; if (string.IsNullOrWhiteSpace(rawValue)) { bindingContext.Result = ModelBindingResult.Success(null); return Task.CompletedTask; } // 清理格式:移除$符号和千分位逗号 var cleanedValue = rawValue.Replace("$", "").Replace(",", ""); if (decimal.TryParse(cleanedValue, NumberStyles.Currency | NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture, out var result)) { bindingContext.Result = ModelBindingResult.Success(result); } else { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "金额格式无效"); bindingContext.Result = ModelBindingResult.Failed(); } return Task.CompletedTask; } }
- 创建绑定器提供者:
public class FormattedDecimalBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); // 针对decimal?类型应用绑定器 if (context.Metadata.ModelType == typeof(decimal?)) return new BinderTypeModelBinder(typeof(FormattedDecimalBinder)); return null; } }
- 在
Program.cs中注册绑定器提供者:
builder.Services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new FormattedDecimalBinderProvider()); });
表单提交Amount字段时,绑定器会先清理格式字符串并转换为decimal?,之后再执行数据验证(包括Precision特性的验证)。
方案二:自定义类型转换器
通过TypeConverter实现字符串到decimal?的转换,步骤如下:
- 创建自定义类型转换器:
public class FormattedDecimalConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string strValue) { if (string.IsNullOrWhiteSpace(strValue)) return null; var cleanedValue = strValue.Replace("$", "").Replace(",", ""); if (decimal.TryParse(cleanedValue, NumberStyles.Currency, CultureInfo.InvariantCulture, out var result)) return result; } return base.ConvertFrom(context, culture, value); } }
- 在
TestClass的Amount属性上添加TypeConverter特性:
public class TestClass { [Precision(18,2)] [TypeConverter(typeof(FormattedDecimalConverter))] public decimal? Amount { get; set; } }
这种方式会在模型绑定阶段自动调用转换器处理字符串,同样能在验证前完成转换。
注意事项
- 两种方案都可根据实际需求调整格式清理逻辑(比如支持其他货币符号、不同的千分位分隔符)
- 如果需要支持多语言,可根据当前请求的
CultureInfo调整解析逻辑,而非固定使用InvariantCulture
内容的提问来源于stack exchange,提问作者Topik
相关产品推荐
相关产品推荐

