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

ASP.NET Core集成Newtonsoft JSON.NET时如何对无效字段触发模型校验错误

.NET Core 3.1 Newtonsoft.Json 未知字段自动生成模型校验错误实现方案

核心思路

默认Newtonsoft.Json反序列化时MissingMemberHandling配置为Ignore,会直接丢弃模型未定义的字段。我们通过自定义契约解析器+模型绑定器的方式,在不影响原有序列化规则、支持可选字段豁免的前提下,自动检测未知字段并写入ModelState。


实现步骤

1. 定义豁免特性

用于标记允许传入额外字段的模型,适配业务中的可选/动态字段场景:

/// <summary>
/// 标记类/接口允许传入未在模型中定义的JSON字段,不触发校验错误
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class AllowExtraJsonFieldsAttribute : Attribute
{
}

2. 自定义契约解析器

继承默认驼峰命名解析器,根据模型是否标记豁免特性,动态切换未知字段处理逻辑:

public class ExtraFieldCheckContractResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);
        // 标记豁免的类型直接跳过未知字段检测
        if (objectType.GetCustomAttributes(typeof(AllowExtraJsonFieldsAttribute), true).Any())
        {
            contract.MissingMemberHandling = MissingMemberHandling.Ignore;
            return contract;
        }
        // 未豁免类型遇到未知字段触发错误回调
        contract.MissingMemberHandling = MissingMemberHandling.Error;
        return contract;
    }
}

3. 自定义模型绑定器

接管JSON参数反序列化流程,收集未知字段错误并写入ModelState:

public class ExtraFieldValidationModelBinder : IModelBinder
{
    private readonly JsonSerializerSettings _serializerSettings;

    public ExtraFieldValidationModelBinder(JsonSerializerSettings serializerSettings)
    {
        _serializerSettings = serializerSettings;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
        var request = bindingContext.HttpContext.Request;
        
        // 非JSON请求直接跳过
        if (!request.ContentType?.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) ?? true)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        try
        {
            using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);
            var jsonContent = await reader.ReadToEndAsync();
            var errorCollection = new Dictionary<string, string>();
            
            var serializer = JsonSerializer.Create(_serializerSettings);
            // 监听序列化错误,收集未知字段信息
            serializer.Error += (_, args) =>
            {
                if (args.ErrorContext.Error is JsonSerializationException ex 
                    && ex.Message.StartsWith("Could not find member", StringComparison.Ordinal))
                {
                    var fieldName = ex.Message.Split('\'')[1];
                    errorCollection[fieldName] = $"参数{fieldName}无效,当前接口不支持该字段";
                    args.ErrorContext.Handled = true;
                }
            };

            var model = serializer.Deserialize(new JsonTextReader(new StringReader(jsonContent)), bindingContext.ModelType);
            bindingContext.Result = ModelBindingResult.Success(model);
            
            // 将收集到的未知字段错误写入模型状态
            foreach (var (key, message) in errorCollection)
            {
                bindingContext.ModelState.AddModelError(key, message);
            }
        }
        catch (Exception ex)
        {
            bindingContext.ModelState.AddModelError(string.Empty, $"JSON参数解析失败:{ex.Message}");
            bindingContext.Result = ModelBindingResult.Failed();
        }
    }
}

4. 自定义模型绑定提供器

用于将自定义绑定器注入到MVC管道中,替换默认JSON绑定逻辑:

public class ExtraFieldValidationModelBinderProvider : IModelBinderProvider
{
    private readonly JsonSerializerSettings _serializerSettings;

    public ExtraFieldValidationModelBinderProvider(JsonSerializerSettings serializerSettings)
    {
        _serializerSettings = serializerSettings;
    }

    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        // 仅处理标记为[FromBody]的复杂类型参数
        if (context.BindingInfo.BindingSource != BindingSource.Body) return null;
        return context.Metadata.IsComplexType && !context.Metadata.IsCollectionType 
            ? new ExtraFieldValidationModelBinder(_serializerSettings) 
            : null;
    }
}

5. 注册服务配置

在Startup.cs的ConfigureServices方法中添加配置,注意将自定义绑定器插入到默认绑定器列表最前端:

public void ConfigureServices(IServiceCollection services)
{
    // 保留原有Newtonsoft.Json配置
    var mvcBuilder = services.AddControllers()
        .AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.ContractResolver = new ExtraFieldCheckContractResolver();
            // 原有配置(日期格式、空值处理、循环引用配置等)保持不变
        });

    // 注入自定义模型绑定器
    services.Configure<MvcOptions>(options =>
    {
        var jsonSettings = new JsonSerializerSettings
        {
            ContractResolver = new ExtraFieldCheckContractResolver()
            // 此处配置和全局Newtonsoft配置保持一致
        };
        options.ModelBinderProviders.Insert(0, new ExtraFieldValidationModelBinderProvider(jsonSettings));
    });
}

使用说明

  • 默认全局规则:所有未标记[AllowExtraJsonFields]的复杂类型参数,传入未定义字段时会自动在ModelState中生成对应错误,配合[ApiController]的自动模型校验机制会直接返回400错误。
  • 豁免配置:如果某个模型需要支持动态/额外字段,直接在类定义上添加[AllowExtraJsonFields]特性即可,会自动回到默认忽略未知字段的逻辑。
  • 嵌套对象支持:嵌套复杂类型的未知字段也会被自动检测,错误Key会按属性层级自动生成。
  • 特殊类型兼容:参数类型为dynamic/JObject时会自动跳过校验,符合动态类型的使用预期。

注意:如果后续调整全局Newtonsoft.Json序列化规则,需要同步更新传入ExtraFieldValidationModelBinderProvider的配置项,避免序列化/反序列化规则不一致。

内容的提问来源于stack exchange,提问作者Vladimir Kruglov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:21:19