AspNetCore:请求含未绑定查询参数时返回400的实现方案
解决方案:拦截未定义查询参数返回400状态码
针对你使用单一请求模型(Mediatr/CQS)且关闭SuppressInferBindingSourcesForParameters的场景,可以通过自定义Action过滤器实现未定义查询参数的校验,具体方案如下:
实现思路
- 在Action执行前,提取请求中所有查询参数的键名
- 解析当前Action的请求模型参数,收集所有标记
[FromQuery]的属性名称(包括基类中的属性) - 对比两组键值,筛选出请求中存在但模型未定义的查询参数
- 若存在此类参数,直接返回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
相关产品推荐
相关产品推荐

