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

AspNetCore:请求含未绑定查询参数时返回400的实现方案

解决方案:拦截未定义查询参数返回400状态码

针对你使用单一请求模型(Mediatr/CQS)且关闭SuppressInferBindingSourcesForParameters的场景,可以通过自定义Action过滤器实现未定义查询参数的校验,具体方案如下:

实现思路

  1. 在Action执行前,提取请求中所有查询参数的键名
  2. 解析当前Action的请求模型参数,收集所有标记[FromQuery]的属性名称(包括基类中的属性)
  3. 对比两组键值,筛选出请求中存在但模型未定义的查询参数
  4. 若存在此类参数,直接返回400 Bad Request

代码实现

1. 自定义Action过滤器

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Reflection;

public class ValidateQueryParametersAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // 获取请求中所有查询参数键
        var requestQueryKeys = context.HttpContext.Request.Query.Keys.ToHashSet(StringComparer.OrdinalIgnoreCase);
        
        // 收集所有允许绑定的查询参数名称
        var allowedQueryKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        foreach (var parameter in context.ActionDescriptor.Parameters)
        {
            CollectFromQueryProperties(parameter.ParameterType, allowedQueryKeys);
        }

        // 找出未定义的查询参数
        var invalidQueryKeys = requestQueryKeys.Except(allowedQueryKeys).ToList();

        if (invalidQueryKeys.Any())
        {
            context.Result = new BadRequestObjectResult(new ProblemDetails
            {
                Status = StatusCodes.Status400BadRequest,
                Title = "无效的查询参数",
                Detail = $"存在未定义的查询参数:{string.Join(", ", invalidQueryKeys)}"
            });
        }

        base.OnActionExecuting(context);
    }

    private void CollectFromQueryProperties(Type type, HashSet<string> allowedKeys)
    {
        if (type == null || type == typeof(object))
            return;

        // 遍历当前类型的属性
        foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var fromQueryAttr = property.GetCustomAttribute<FromQueryAttribute>();
            if (fromQueryAttr != null)
            {
                // 优先使用[FromQuery]指定的名称,否则用属性名
                var paramName = string.IsNullOrEmpty(fromQueryAttr.Name) 
                    ? property.Name 
                    : fromQueryAttr.Name;
                allowedKeys.Add(paramName);
            }
        }

        // 递归处理基类的属性(适配继承场景)
        CollectFromQueryProperties(type.BaseType, allowedKeys);
    }
}

2. 注册过滤器

可以选择全局生效或仅针对特定控制器/Action生效:

全局注册(Program.cs)

builder.Services.AddControllers(options =>
{
    options.SuppressInferBindingSourcesForParameters = true;
    // 添加自定义过滤器
    options.Filters.Add<ValidateQueryParametersAttribute>();
});

单独标记控制器/Action

[HttpGet]
[ValidateQueryParameters] // 仅对当前Action生效
[Produces(MediaTypeNames.Application.Json)]
public Task<IActionResult> GetExamplesAsync(GetExamplesQuery request, CancellationToken cancelToken) 
    => HandleAsync(request, cancelToken);

关键细节说明

  • 支持继承层级:递归遍历请求模型的基类,确保PaginatedQuery这类基类中的[FromQuery]属性也被纳入允许列表
  • 忽略大小写:使用StringComparer.OrdinalIgnoreCase处理查询参数,符合HTTP参数的常规处理逻辑
  • 适配自定义参数名:若属性通过[FromQuery(Name = "xxx")]指定了自定义参数名,会优先使用该名称而非属性本身的名称

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 08:25:42