在.NET中读取名称含冒号的Query参数并验证后缀的实现方法
实现方案
1. 定义基础结构
先定义参数接收类和允许的操作符范围:
// 时间查询参数接收类 public class EventDateTimeFilter { public DateTimeOffset Time { get; set; } // 操作符,无后缀时默认值为eq public string Operator { get; set; } = "eq"; }
允许的操作符可以定义为常量数组,后续绑定校验时直接使用:
private static readonly string[] _allowedOperators = new[] { "eq", "gte", "lte", "gt", "lt", "neq" };
2. 实现自定义模型绑定器
这个方案的核心是通过自定义模型绑定器遍历查询字符串,匹配对应前缀的参数并解析后缀:
using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Globalization; public class EventDateTimeBinder : IModelBinder { private const string _paramPrefix = "eventCreatedDateTime"; private static readonly string[] _allowedOperators = new[] { "eq", "gte", "lte", "gt", "lt", "neq" }; public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); // 遍历所有查询参数键匹配目标参数 foreach (var queryKey in bindingContext.HttpContext.Request.Query.Keys) { if (!queryKey.StartsWith(_paramPrefix, StringComparison.OrdinalIgnoreCase)) continue; string op = null; // 解析带操作符的后缀 if (queryKey.Contains(':')) { var keyParts = queryKey.Split(':', StringSplitOptions.RemoveEmptyEntries); if (keyParts.Length != 2) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "参数格式错误,正确格式为:eventCreatedDateTime[:操作符]"); return Task.CompletedTask; } op = keyParts[1].ToLower().Trim(); // 校验操作符合法性 if (!_allowedOperators.Contains(op)) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, $"不支持的操作符 {op},允许值为:{string.Join("、", _allowedOperators)}"); return Task.CompletedTask; } } // 提取并解析时间值 var valueResult = bindingContext.ValueProvider.GetValue(queryKey); if (!DateTimeOffset.TryParse(valueResult.FirstValue, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var time)) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "时间格式无效,请使用ISO 8601标准格式"); return Task.CompletedTask; } // 返回绑定结果 var filter = new EventDateTimeFilter { Time = time, Operator = op ?? "eq" }; bindingContext.Result = ModelBindingResult.Success(filter); return Task.CompletedTask; } // 未传该参数时返回空 bindingContext.Result = ModelBindingResult.Success(null); return Task.CompletedTask; } }
3. 接口中使用绑定器
直接在接口参数上指定绑定器即可:
[ApiController] [Route("api/[controller]")] public class MyController : ControllerBase { [HttpGet] public IActionResult Query( [FromQuery] string param1, [FromQuery] string param2, [FromQuery, ModelBinder(BinderType = typeof(EventDateTimeBinder))] EventDateTimeFilter eventCreatedDateTime, [FromQuery] string param4) { // 业务逻辑直接使用 eventCreatedDateTime.Operator 和 eventCreatedDateTime.Time 即可 return Ok(); } }
4. Swagger兼容处理
默认Swagger无法识别带后缀的参数格式,需要添加操作过滤器自定义参数描述:
using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; public class EventDateTimeSwaggerFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var targetParam = context.MethodInfo.GetParameters().FirstOrDefault(p => p.ParameterType == typeof(EventDateTimeFilter)); if (targetParam == null) return; // 移除默认生成的错误参数 var defaultParam = operation.Parameters.FirstOrDefault(p => p.Name.Equals("eventCreatedDateTime", StringComparison.OrdinalIgnoreCase)); if (defaultParam != null) operation.Parameters.Remove(defaultParam); // 添加自定义参数说明 operation.Parameters.Add(new OpenApiParameter { Name = "eventCreatedDateTime[:op]", In = ParameterLocation.Query, Description = "时间查询参数,可选操作符后缀支持eq/gte/lte/gt/lt/neq,默认eq,示例:eventCreatedDateTime:gte=2021-04-01T14:12:56+01:00", Required = false, Schema = new OpenApiSchema { Type = "string", Format = "date-time" } }); } }
在Program.cs中注册过滤器:
builder.Services.AddSwaggerGen(opt => { opt.OperationFilter<EventDateTimeSwaggerFilter>(); });
如果有多个同类型参数需要支持这种后缀格式,可以将绑定器改造为通用版本,把参数前缀、允许操作符列表改为可配置项即可。
内容的提问来源于stack exchange,提问作者FoxHound
相关产品推荐
相关产品推荐

