.NET 9 OpenAPI组件抛出System.FormatException问题咨询
触发System.FormatException的核心原因是:.NET 9内置OpenAPI组件在处理[Range(1, double.MaxValue)]特性时,错误地将double.MaxValue对应的科学计数法字符串"1.7976931348623157E+308"传入Decimal.Parse方法——而decimal类型的默认解析逻辑不支持科学计数法格式,因此抛出格式错误。
你的Price字段是decimal类型,但Range特性的上限使用了double.MaxValue。当OpenAPI组件生成Schema时,会尝试将Range的上下限转换为与属性类型匹配的数值,这里它把double.MaxValue转成字符串后直接调用Decimal.Parse,最终引发异常。
针对你无法使用decimal.MaxValue的限制,提供三种可行方案:
方案1:自定义decimal专属范围验证特性
自己实现一个验证特性,跳过OpenAPI组件的错误类型转换逻辑:
using System.ComponentModel.DataAnnotations; public class DecimalMinRangeAttribute : ValidationAttribute { private readonly decimal _min; public DecimalMinRangeAttribute(int min) { _min = min; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value is not decimal price) return new ValidationResult("Price must be a valid decimal number"); if (price < _min) return new ValidationResult($"Price cannot be less than {_min}"); // decimal的最大值远小于double.MaxValue,实际业务中不可能超出,无需验证上限 return ValidationResult.Success; } }
替换原有Range特性:
class MyModel { [DecimalMinRange(1)] public decimal Price { get; set; } }
方案2:修改OpenAPI Schema生成规则
通过自定义Schema过滤器,修正Range特性的处理逻辑:
using Microsoft.AspNetCore.OpenApi; using System.ComponentModel.DataAnnotations; using System.Text.Json.Schema; builder.Services.AddOpenApi(options => { options.SchemaGenerator.ConfigureSchema += (context, schema) => { if (context.Type != typeof(decimal)) return; var rangeAttr = context.Member?.GetCustomAttributes(typeof(RangeAttribute), false) .Cast<RangeAttribute>() .FirstOrDefault(); if (rangeAttr?.Maximum is double max && max == double.MaxValue) { if (schema is JsonNumberSchema numberSchema) { // 将上限替换为decimal.MaxValue,避免科学计数法解析错误 numberSchema.Maximum = decimal.MaxValue; } } }; });
该方案直接修正OpenAPI生成的Schema,既保留原有验证逻辑,又避免解析异常。
方案3:切换到FluentValidation
使用FluentValidation替代DataAnnotations,它的规则更灵活且能被OpenAPI组件正确处理:
- 安装NuGet包:
FluentValidation.AspNetCore - 注册服务:
builder.Services.AddFluentValidationAutoValidation();
- 编写验证规则:
using FluentValidation; public class MyModelValidator : AbstractValidator<MyModel> { public MyModelValidator() { RuleFor(m => m.Price) .GreaterThanOrEqualTo(1) .LessThanOrEqualTo((decimal)double.MaxValue); } }
移除原有[Range]特性即可。
decimal.MaxValue的字符串表示为普通数字格式(79228162514264337593543950335),而非科学计数法,因此OpenAPI组件可以正常解析,不会触发异常。
内容的提问来源于stack exchange,提问作者Parsa99

