添加JsonSerializer转换器后.NET 6中FromBody返回null的问题求助
问题与解决方案
问题现象
在.NET 6项目的Startup中添加自定义JsonSerializer转换器后,通过AJAX提交数据时,控制器方法中[FromBody]标记的参数始终为null;注释掉这些转换器后,代码可正常运行。
相关代码
Startup配置
services.AddControllersWithViews().AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new DateTimeConverterFactory()); options.JsonSerializerOptions.Converters.Add(new DecimalConverterFactory()); options.JsonSerializerOptions.Converters.Add(new IntConverterFactory()); });
AJAX提交代码
function PerformAction(url, data) { var model = {}; model.ListOfIds = data; var testModel = JSON.stringify(model); $.ajax({ url: url, type: "POST", Traditional: true, data: testModel, dataType: "json", contentType: "application/json; charset=utf-8", success: function (ValidationErrors) { // 业务逻辑处理 } }); }
控制器代码
[HttpPost] public IActionResult AjaxReject([FromBody] ListModel testModel) { var listOfIds = testModel?.ListOfIds; // testModel始终为null } public class ListModel { public List<int> ListOfIds { get; set; } }
示例转换器代码(IntConverterFactory)
public class IntConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { return typeToConvert == typeof(int) || typeToConvert == typeof(int?); } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { if (typeToConvert == typeof(int)) { return new IntConverter(); } else if (typeToConvert == typeof(int?)) { return new NullableIntConverter(); } throw new NotSupportedException("当前转换器工厂不支持该类型"); } } public class IntConverter : JsonConverter<int> { public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var stringValue = reader.GetString(); return IntExtensions.GetFormattedInt(stringValue); } public override void Write( Utf8JsonWriter writer, int objectToWrite, JsonSerializerOptions options) { writer.WriteNumberValue(objectToWrite); } } public class NullableIntConverter : JsonConverter<int?> { public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.GetString() is { } value) { return IntExtensions.GetFormattedInt(value); } return null; } public override void Write( Utf8JsonWriter writer, int? objectToWrite, JsonSerializerOptions options) { if (objectToWrite is { }) writer.WriteNumberValue((int)objectToWrite); else writer.WriteNullValue(); } }
问题原因
自定义转换器的Read方法存在逻辑缺陷:AJAX提交的JSON数据中,ListOfIds的元素是JSON数字类型(比如[1,2,3]),但转换器直接调用reader.GetString()获取值——只有当JSON Token类型为字符串时,GetString()才会返回有效值,数字类型调用该方法会返回null,进而导致转换失败。一旦单个属性转换失败,整个模型绑定就会终止,最终testModel为null。
Decimal转换器也存在同样问题,若遇到JSON数字类型的decimal值,GetString()无法获取有效值,导致转换出错。
解决方案
修改转换器的Read方法,增加对JSON数字类型的判断和处理,同时兼容字符串类型输入,确保两种格式都能正常转换。
修改后的IntConverter代码
public class IntConverter : JsonConverter<int> { public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { string stringValue; // 根据当前Token类型分支处理 if (reader.TokenType == JsonTokenType.String) { stringValue = reader.GetString(); } else if (reader.TokenType == JsonTokenType.Number) { // 若无需特殊格式处理,直接读取数值即可 return reader.GetInt32(); // 若需通过IntExtensions处理,可改为: // stringValue = reader.GetInt32().ToString(); } else { throw new JsonException($"不支持的Token类型:{reader.TokenType}"); } return IntExtensions.GetFormattedInt(stringValue); } public override void Write( Utf8JsonWriter writer, int objectToWrite, JsonSerializerOptions options) { writer.WriteNumberValue(objectToWrite); } } public class NullableIntConverter : JsonConverter<int?> { public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) { return null; } string stringValue; if (reader.TokenType == JsonTokenType.String) { stringValue = reader.GetString(); if (string.IsNullOrWhiteSpace(stringValue)) { return null; } } else if (reader.TokenType == JsonTokenType.Number) { return reader.GetInt32(); // 若需通过IntExtensions处理,可改为: // stringValue = reader.GetInt32().ToString(); } else { throw new JsonException($"不支持的Token类型:{reader.TokenType}"); } return IntExtensions.GetFormattedInt(stringValue); } public override void Write( Utf8JsonWriter writer, int? objectToWrite, JsonSerializerOptions options) { if (objectToWrite is { }) writer.WriteNumberValue((int)objectToWrite); else writer.WriteNullValue(); } }
修改后的DecimalConverter代码
public class DecimalConverter : JsonConverter<decimal> { public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { string decimalToParse; if (reader.TokenType == JsonTokenType.String) { decimalToParse = reader.GetString(); } else if (reader.TokenType == JsonTokenType.Number) { // 若无需特殊格式处理,直接读取数值即可 return reader.GetDecimal(); // 若需通过DecimalExtensions处理,可改为: // decimalToParse = reader.GetDecimal().ToString(); } else { throw new JsonException($"不支持的Token类型:{reader.TokenType}"); } return DecimalExtensions.GetFormattedDecimal(decimalToParse); } public override void Write( Utf8JsonWriter writer, decimal objectToWrite, JsonSerializerOptions options) { writer.WriteNumberValue(objectToWrite); } } public class NullableDecimalConverter : JsonConverter<decimal?> { public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) { return null; } string value; if (reader.TokenType == JsonTokenType.String) { value = reader.GetString(); if (string.IsNullOrWhiteSpace(value)) { return null; } } else if (reader.TokenType == JsonTokenType.Number) { return reader.GetDecimal(); // 若需通过DecimalExtensions处理,可改为: // value = reader.GetDecimal().ToString(); } else { throw new JsonException($"不支持的Token类型:{reader.TokenType}"); } return DecimalExtensions.GetFormattedDecimal(value); } public override void Write( Utf8JsonWriter writer, decimal? objectToWrite, JsonSerializerOptions options) { if (objectToWrite is { }) writer.WriteNumberValue(objectToWrite.Value); else writer.WriteNullValue(); } }
补充说明
- 如果
IntExtensions.GetFormattedInt和DecimalExtensions.GetFormattedDecimal方法需要处理特殊格式字符串(比如带千分位、货币符号的字符串),则保留将数字转换为字符串后传入的逻辑;如果只是普通数值转换,直接调用reader.GetInt32()/reader.GetDecimal()效率更高。 - 建议在
DateTimeConverterFactory中也做类似兼容处理,避免遇到相同问题。
内容的提问来源于stack exchange,提问作者Gauravsa
相关产品推荐
相关产品推荐

