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

ASP.NET Core 7如何检测Http POST请求中的额外属性?

在ASP.NET Core 7中检测[FromBody]绑定的额外属性

默认情况下,ASP.NET Core使用的System.Text.Json序列化器会自动忽略请求体中目标类未定义的额外属性,不会触发任何错误。如果需要检测并拦截这类请求,可以通过以下几种方式实现:

方法一:修改JSON序列化配置,启用未知属性报错

直接配置全局Json选项,关闭"忽略未知属性"的开关,这样当请求体包含未定义属性时,反序列化会直接抛出异常,返回400 Bad Request。

在Program.cs中添加配置:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // 关闭忽略未知属性,遇到未定义属性时抛出JsonException
        options.JsonSerializerOptions.IgnoreUnknownValues = false;
    });

这种方式实现简单,但错误信息比较笼统,无法明确指出具体是哪个属性未定义。

方法二:使用自定义资源过滤器检测未知属性

通过自定义过滤器,可以在模型绑定前读取请求体,解析JSON后对比目标类的属性,精准找出未定义的属性并返回自定义错误信息。

  1. 实现自定义资源过滤器:
public class DetectUnknownPropertiesFilter : IAsyncResourceFilter
{
    public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
    {
        // 定位到目标参数类型(这里以SomeData为例)
        var targetParam = context.ActionDescriptor.Parameters
            .FirstOrDefault(p => p.ParameterType == typeof(SomeData));
        
        if (targetParam == null)
        {
            await next();
            return;
        }

        // 缓存请求体,避免后续绑定无法读取
        var originalBody = context.HttpContext.Request.Body;
        try
        {
            using var memoryStream = new MemoryStream();
            await originalBody.CopyToAsync(memoryStream);
            memoryStream.Position = 0;
            context.HttpContext.Request.Body = memoryStream;

            // 读取并解析JSON内容
            var jsonContent = await new StreamReader(memoryStream).ReadToEndAsync();
            memoryStream.Position = 0; // 重置流位置,供后续模型绑定使用
            
            using var jsonDoc = JsonDocument.Parse(jsonContent);
            var rootElement = jsonDoc.RootElement;

            // 获取目标类的所有属性名(考虑JsonPropertyName特性和序列化命名策略)
            var knownProps = typeof(SomeData).GetProperties()
                .Select(prop =>
                {
                    var jsonAttr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
                    return jsonAttr?.Name 
                           ?? context.HttpContext.RequestServices
                                   .GetRequiredService<IOptions<JsonOptions>>()
                                   .Value.JsonSerializerOptions.PropertyNamingPolicy
                                   .ConvertName(prop.Name);
                })
                .ToHashSet(StringComparer.OrdinalIgnoreCase);

            // 遍历JSON属性,检查是否存在未定义项
            foreach (var jsonProp in rootElement.EnumerateObject())
            {
                if (!knownProps.Contains(jsonProp.Name))
                {
                    context.ModelState.AddModelError("", $"请求包含未定义的属性:{jsonProp.Name}");
                    context.Result = new BadRequestObjectResult(context.ModelState);
                    return;
                }
            }

            await next();
        }
        finally
        {
            context.HttpContext.Request.Body = originalBody;
        }
    }
}
  1. 注册过滤器(全局或控制器级):
    全局注册在Program.cs:
builder.Services.AddControllers(options =>
{
    options.Filters.Add<DetectUnknownPropertiesFilter>();
})
.AddJsonOptions(options =>
{
    // 保持默认忽略未知属性,避免提前抛出异常
    options.JsonSerializerOptions.IgnoreUnknownValues = true;
});

如果只需要针对特定控制器生效,可在控制器上添加[ServiceFilter(typeof(DetectUnknownPropertiesFilter))]特性。

这种方式可以精准控制检测逻辑,返回友好的错误信息,灵活性高。

方法三:使用自定义JsonConverter实现严格反序列化

通过自定义Json转换器,在反序列化过程中直接检测未知属性,抛出明确的错误。

  1. 实现自定义转换器:
public class StrictJsonConverter<T> : JsonConverter<T> where T : class, new()
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // 解析JSON文档
        using var jsonDoc = JsonDocument.ParseValue(ref reader);
        var rootElement = jsonDoc.RootElement;

        // 获取目标类的已知属性名
        var knownProps = typeToConvert.GetProperties()
            .Select(prop =>
            {
                var jsonAttr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
                return jsonAttr?.Name ?? options.PropertyNamingPolicy.ConvertName(prop.Name);
            })
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        // 检查未知属性
        foreach (var jsonProp in rootElement.EnumerateObject())
        {
            if (!knownProps.Contains(jsonProp.Name))
            {
                throw new JsonException($"请求中存在未定义的属性:{jsonProp.Name}");
            }
        }

        // 执行正常反序列化
        return jsonDoc.Deserialize<T>(options);
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, options);
    }
}
  1. 注册转换器:
    在Program.cs中配置,可针对全局或特定类型:
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // 为SomeData类型添加严格转换器
        options.JsonSerializerOptions.Converters.Add(new StrictJsonConverter<SomeData>());
    });

这种方式适合需要对特定类型进行严格校验的场景,错误信息明确,但需要为每个目标类型单独注册转换器(或通过泛型批量处理)。


内容的提问来源于stack exchange,提问作者Grant Nilsson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:36:02