【问题标题】:Inifinite loop during model binding when using [ModelBinder] attribute使用 [ModelBinder] 属性时模型绑定期间的无限循环
【发布时间】:2018-11-14 23:59:19
【问题描述】:

我最初在 GitHub 上发布了这个问题:https://github.com/aspnet/Mvc/issues/8723

这里有一个 GitHub 存储库,其中重现了该问题: https://github.com/Costo/aspnetcore-binding-bug

我正在使用 ASP.NET Core 2.2 Preview 3。

在“子”模型数组的属性上使用自定义模型绑定器(带有 [ModelBinder] 属性)时,请求的模型绑定阶段进入无限循环。看这个截图:

如果在顶级模型属性上使用自定义模型绑定器效果很好,但我想了解为什么它在子模型数组中使用时不起作用。对此的任何帮助将不胜感激。

谢谢!


这是模型、控制器、视图和自定义绑定器的代码:

模型:

public class TestModel
{
    public TestInnerModel[] InnerModels { get; set; } = new TestInnerModel[0];

    [ModelBinder(BinderType = typeof(NumberModelBinder))]
    public decimal TopLevelRate { get; set; }
}

public class TestInnerModel
{
    public TestInnerModel()
    {
    }

    [ModelBinder(BinderType = typeof(NumberModelBinder))]
    public decimal Rate { get; set; }
}

自定义模型绑定器(特意简化为没什么特别的):

public class NumberModelBinder : IModelBinder
{
    private readonly NumberStyles _supportedStyles = NumberStyles.Float | NumberStyles.AllowThousands;
    private DecimalModelBinder _innerBinder;

    public NumberModelBinder(ILoggerFactory loggerFactory)
    {
        _innerBinder = new DecimalModelBinder(_supportedStyles, loggerFactory);
    }

    /// <inheritdoc />
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        return _innerBinder.BindModelAsync(bindingContext);
    }
}

控制器:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View(new TestModel
        {
            TopLevelRate = 20m,
            InnerModels = new TestInnerModel[]
            {
                new TestInnerModel { Rate = 2.0m },
                new TestInnerModel { Rate = 0.2m }
            }
        });
    }

    [HttpPost]
    public IActionResult Index(TestModel model)
    {
        return Ok();
    }
}

剃刀视图:

@model TestModel;

<form asp-controller="Home" asp-action="Index" method="post" role="form">
    <div>
        <input asp-for="@Model.TopLevelRate" type="number" min="0" step=".01" />

    </div>
    <div>
        @for (var i = 0; i < Model.InnerModels.Length; i++)
        {
            <input asp-for="@Model.InnerModels[i].Rate" type="number" min="0" step=".01" />
        }
    </div>

    <input type="submit" value="Go" />
</form>

【问题讨论】:

    标签: asp.net-core-mvc


    【解决方案1】:

    solution 已发布到 GitHub 问题:

    @Costo 问题是您没有通知模型绑定系统绑定器使用值提供程序。 ComplexTypeModelBinder 始终相信数据可用于下一个 TestInnerModel 实例,并且最外层的活页夹 (CollectionModelBinder) 会一直运行 - 永远。为了解决这个问题,

    [MyModelBinder]
    public decimal Rate { get; set; }
    
    private class MyModelBinderAttribute : ModelBinderAttribute
    {
        public MyModelBinderAttribute()
            : base(typeof(NumberModelBinder))
        {
            BindingSource = BindingSource.Form;
        }
    }
    

    换句话说,BindingSource.Custom 默认使用 [ModelBinder] 在这种情况下是不正确的。幸运的是,容器中 POCO 类型属性上的自定义模型绑定器应该是极少数重要的情况之一。

    【讨论】:

      猜你喜欢
      • 2017-09-10
      • 1970-01-01
      • 1970-01-01
      • 2016-05-15
      • 2020-07-01
      • 2014-09-11
      • 2015-12-28
      • 2015-08-19
      • 2014-04-20
      相关资源
      最近更新 更多