.NET 5查询字符串参数绑定字典时自动补[eq]运算符方案问询
实现方案
方案1:中间件全局改写查询参数
虽然context.Request.Query默认是只读的,但可以直接对它重新赋值为新构造的QueryCollection对象,实现全局的查询参数归一化处理,步骤如下:
- 新建中间件类,处理查询参数转换逻辑
using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; public class QueryOperatorNormalizationMiddleware { private readonly RequestDelegate _next; public QueryOperatorNormalizationMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var originalQuery = context.Request.Query; var modifiedQuery = new Dictionary<string, StringValues>(); foreach (var (key, value) in originalQuery) { // 判断是否已经带有运算符后缀 if (!key.Contains('[') && !key.Contains(']')) { // 无运算符的参数追加[eq]后缀 var newKey = $"{key}[eq]"; modifiedQuery[newKey] = value; } else { // 已有运算符的参数保留原键 modifiedQuery[key] = value; } } // 替换原查询参数集合 context.Request.Query = new QueryCollection(modifiedQuery); await _next(context); } }
- 在Program.cs中注册中间件,注意要放在路由、控制器相关中间件之前:
var builder = WebApplication.CreateBuilder(args); // 其他服务注册... var app = builder.Build(); // 注册在最前面或者UseRouting之前即可 app.UseMiddleware<QueryOperatorNormalizationMiddleware>(); app.UseRouting(); // 其他中间件... app.MapControllers(); app.Run();
该方案全局生效,所有接口的查询参数都会自动适配规则,无需修改模型定义,只需保留字典类型的属性即可正常绑定。
方案2:自定义模型绑定器(更灵活)
如果不需要全局生效,只针对指定的请求模型生效,可以使用自定义模型绑定器,不需要修改全局查询参数:
- 实现自定义模型绑定器
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Primitives; public class OperatorDictionaryModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(Dictionary<string, int>)) { return Task.CompletedTask; } var modelName = bindingContext.ModelName; var resultDict = new Dictionary<string, int>(); // 处理带运算符的参数,比如price[gt] var matchingKeys = bindingContext.HttpContext.Request.Query .Where(q => q.Key.StartsWith($"{modelName}[", StringComparison.OrdinalIgnoreCase) && q.Key.EndsWith("]")) .ToList(); foreach (var (key, value) in matchingKeys) { var op = key.Substring(modelName.Length + 1, key.Length - modelName.Length - 2); if (int.TryParse(value, out var numValue)) { resultDict[op.ToLower()] = numValue; } } // 处理无运算符的默认参数,比如price=10 var defaultValue = bindingContext.ValueProvider.GetValue(modelName); if (defaultValue != ValueProviderResult.None && int.TryParse(defaultValue.FirstValue, out var eqValue)) { resultDict["eq"] = eqValue; } bindingContext.Result = ModelBindingResult.Success(resultDict); return Task.CompletedTask; } }
- 在请求模型的属性上指定绑定器即可:
public class BookRequestModel { [FromQuery] [ModelBinder(BinderType = typeof(OperatorDictionaryModelBinder))] public Dictionary<string, int> Price { get; set; } }
该方案不会影响其他接口的查询参数逻辑,适合仅部分接口需要该规则的场景。
选型建议
- 全项目统一使用查询运算符规则选方案1
- 仅部分接口需要该能力选方案2
内容的提问来源于stack exchange,提问作者adnan kamili
相关产品推荐
相关产品推荐

