ASP.NET Minimal API多维数组参数绑定问题求助
问题:Minimal API无法绑定多维数组查询参数
在ASP.NET Core Controller中,请求/multi?vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1][1]=7可以正常绑定int[][]类型的查询参数:
[Route("/multi")] public class MultiController : Controller { [HttpGet("")] public IActionResult Index([FromQuery] int[][] vals) { return this.Ok(string.Join(" ", vals.Select(x => string.Join(":", x)))); } }
但在Minimal API中使用相同的参数绑定方式时,会抛出错误:
InvalidOperationException: No public static bool int[].TryParse(string, out int[]) method found for vals. Microsoft.AspNetCore.Http.RequestDelegateFactory.BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, RequestDelegateFactoryContext factoryContext, string source)
这是因为Minimal API默认对值类型/数组类型使用TryParse方式绑定,而int[][]没有内置的TryParse方法。不想通过大型DTO的BindAsync来处理,需要直接支持int[][]作为查询参数的方案。
解决方案
方案1:使用简单DTO包装数组
创建仅包含数组属性的DTO,Minimal API会自动复用Controller的模型绑定逻辑处理多维数组:
public class MultiArrayDto { public int[][] Vals { get; set; } }
修改Minimal API端点:
app.MapGet("/multi-minimal", ([FromQuery] MultiArrayDto dto) => $"Hello World! {string.Join(":::", dto.Vals.Select(x => string.Join(":", x)))}");
请求/multi-minimal?vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1][1]=7即可正常绑定。
方案2:自定义模型绑定器
如果不想使用DTO,可以创建针对int[][]的自定义模型绑定器,让Minimal API直接识别并绑定:
- 实现模型绑定器:
public class IntJaggedArrayModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) { bindingContext.Result = ModelBindingResult.Success(null); return Task.CompletedTask; } var queryKeys = bindingContext.HttpContext.Request.Query.Keys; var arrayData = new Dictionary<int, Dictionary<int, int>>(); foreach (var key in queryKeys) { var match = System.Text.RegularExpressions.Regex.Match(key, @"^(\w+)\[(\d+)\]\[(\d+)\]$"); if (match.Success && int.TryParse(match.Groups[2].Value, out int outerIndex) && int.TryParse(match.Groups[3].Value, out int innerIndex) && int.TryParse(bindingContext.HttpContext.Request.Query[key], out int value)) { if (!arrayData.ContainsKey(outerIndex)) arrayData[outerIndex] = new Dictionary<int, int>(); arrayData[outerIndex][innerIndex] = value; } } if (arrayData.Count == 0) { bindingContext.Result = ModelBindingResult.Success(null); return Task.CompletedTask; } var maxOuterIdx = arrayData.Keys.Max(); var result = new int[maxOuterIdx + 1][]; foreach (var outerEntry in arrayData) { var maxInnerIdx = outerEntry.Value.Keys.Max(); result[outerEntry.Key] = new int[maxInnerIdx + 1]; foreach (var innerEntry in outerEntry.Value) { result[outerEntry.Key][innerEntry.Key] = innerEntry.Value; } } bindingContext.Result = ModelBindingResult.Success(result); return Task.CompletedTask; } }
- 实现绑定器提供器并注册到服务:
public class IntJaggedArrayModelBinderProvider : IModelBinderProvider { public IModelBinder? GetBinder(ModelBinderProviderContext context) { if (context?.Metadata.ModelType == typeof(int[][])) return new IntJaggedArrayModelBinder(); return null; } } // 在Program中注册 var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new IntJaggedArrayModelBinderProvider()); });
- 在Minimal API中使用绑定器:
app.MapGet("/multi-minimal", ([FromQuery][ModelBinder(typeof(IntJaggedArrayModelBinder))] int[][] vals) => $"Hello World! {string.Join(":::", vals?.Select(x => string.Join(":", x)) ?? Enumerable.Empty<string>())}");
方案3:直接从HttpContext解析查询参数
跳过内置绑定逻辑,手动从HttpContext的查询集合中解析多维数组:
app.MapGet("/multi-minimal", (HttpContext context) => { var query = context.Request.Query; var arrayData = new Dictionary<int, Dictionary<int, int>>(); foreach (var key in query.Keys) { var match = System.Text.RegularExpressions.Regex.Match(key, @"^vals\[(\d+)\]\[(\d+)\]$"); if (match.Success && int.TryParse(match.Groups[1].Value, out int outerIdx) && int.TryParse(match.Groups[2].Value, out int innerIdx) && int.TryParse(query[key], out int val)) { if (!arrayData.ContainsKey(outerIdx)) arrayData[outerIdx] = new Dictionary<int, int>(); arrayData[outerIdx][innerIdx] = val; } } if (arrayData.Count == 0) return "No values provided"; var maxOuter = arrayData.Keys.Max(); int[][] vals = new int[maxOuter + 1][]; foreach (var outerEntry in arrayData) { var maxInner = outerEntry.Value.Keys.Max(); vals[outerEntry.Key] = new int[maxInner + 1]; foreach (var innerEntry in outerEntry.Value) { vals[outerEntry.Key][innerEntry.Key] = innerEntry.Value; } } return $"Hello World! {string.Join(":::", vals.Select(x => string.Join(":", x)))}"; });
内容的提问来源于stack exchange,提问作者bep
相关产品推荐
相关产品推荐

