InvariantDecimalModelBinderProvider.cs
1,804 bytes
| 1 | using System.Globalization; |
|---|---|
| 2 | using Microsoft.AspNetCore.Mvc.ModelBinding; |
| 3 | |
| 4 | namespace WebApp; |
| 5 | |
| 6 | /// <summary> |
| 7 | /// Ensures decimal values from HTML number inputs (which always send dot-separated values) |
| 8 | /// bind correctly regardless of the server's request culture (e.g. et-EE uses comma). |
| 9 | /// </summary> |
| 10 | public class InvariantDecimalModelBinderProvider : IModelBinderProvider |
| 11 | { |
| 12 | public IModelBinder? GetBinder(ModelBinderProviderContext context) |
| 13 | { |
| 14 | if (context.Metadata.ModelType == typeof(decimal) || context.Metadata.ModelType == typeof(decimal?)) |
| 15 | { |
| 16 | return new InvariantDecimalModelBinder(); |
| 17 | } |
| 18 | |
| 19 | return null; |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | public class InvariantDecimalModelBinder : IModelBinder |
| 24 | { |
| 25 | public Task BindModelAsync(ModelBindingContext bindingContext) |
| 26 | { |
| 27 | var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); |
| 28 | if (valueResult == ValueProviderResult.None) |
| 29 | return Task.CompletedTask; |
| 30 | |
| 31 | var value = valueResult.FirstValue; |
| 32 | if (string.IsNullOrWhiteSpace(value)) |
| 33 | { |
| 34 | if (bindingContext.ModelType == typeof(decimal?)) |
| 35 | { |
| 36 | bindingContext.Result = ModelBindingResult.Success(null); |
| 37 | } |
| 38 | |
| 39 | return Task.CompletedTask; |
| 40 | } |
| 41 | |
| 42 | // Normalize: replace comma with dot so InvariantCulture can parse both formats |
| 43 | value = value.Replace(',', '.'); |
| 44 | |
| 45 | if (decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) |
| 46 | { |
| 47 | bindingContext.Result = ModelBindingResult.Success(result); |
| 48 | } |
| 49 | else |
| 50 | { |
| 51 | bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "Invalid number format."); |
| 52 | } |
| 53 | |
| 54 | return Task.CompletedTask; |
| 55 | } |
| 56 | } |
| 57 | |