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

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直接识别并绑定:

  1. 实现模型绑定器:
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;
    }
}
  1. 实现绑定器提供器并注册到服务:
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());
});
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 14:37:10