【发布时间】:2018-03-31 05:52:39
【问题描述】:
我有一个动态列表的动态列表,其中有 <input />s 需要发布到 MVC 控制器/动作并绑定为类型化对象。我的问题的症结在于我无法弄清楚如何在我的custom model binder 中手动选择任意 POSTed 表单值。详情如下。
我有一个美国各州的列表,每个州都有一个城市列表。州和城市都可以动态添加、删除和重新排序。所以像:
public class ConfigureStatesModel
{
public List<State> States { get; set; }
}
public class State
{
public string Name { get; set; }
public List<City> Cities { get; set; }
}
public class City
{
public string Name { get; set; }
public int Population { get; set; }
}
GET:
public ActionResult Index()
{
var csm = new ConfigureStatesModel(); //... populate model ...
return View("~/Views/ConfigureStates.cshtml", csm);
}
ConfigureStates.cshtml:
@model Models.ConfigureStatesModel
@foreach (var state in Model.States)
{
<input name="stateName" type="text" value="@state.Name" />
foreach (var city in state.Cities)
{
<input name="cityName" type="text" value="@city.Name" />
<input name="cityPopulation" type="text" value="@city.Population" />
}
}
(有更多的标记和 javascript,但为了简洁/简单起见,我将其省略。)
然后所有表单输入都被 POST 到服务器,因此(由 Chrome 开发工具解析):
stateName: California
cityName: Sacramento
cityPopulation: 1000000
cityName: San Francisco
cityPopulation: 2000000
stateName: Florida
cityName: Miami
cityPopulation: 3000000
cityName: Orlando
cityPopulation: 4000000
我需要捕获表单值,理想情况下绑定为List<State>(或等效为ConfigureStatesModel),如下所示:
[HttpPost]
public ActionResult Save(List<State> states)
{
//do some stuff
}
自定义模型绑定器似乎是适合这项工作的工具。但我不知道如何知道哪些城市名称和城市人口属于哪些州名称。也就是说,我可以看到所有已发布的表单键和值,但我看不到了解它们关系的方法:
public class StatesBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
//California, Florida
List<string> stateNames = controllerContext.HttpContext.Request.Form.GetValues("stateName").ToList();
//Sacramento, San Francisco, Miami, Orlando
List<string> cityNames = controllerContext.HttpContext.Request.Form.GetValues("cityName").ToList();
//1000000, 2000000, 3000000, 4000000
List<int> cityPopulations = controllerContext.HttpContext.Request.Form.GetValues("cityPopulation")
.Select(p => int.Parse(p)).ToList();
// ... build List<State> ...
}
}
如果我能知道所有值相对于所有其他表单值的顺序,那就足够了。我看到的唯一方法是查看原始请求流,如下所示:
Request.InputStream.Seek(0, SeekOrigin.Begin);
string urlEncodedFormData = new StreamReader(Request.InputStream).ReadToEnd();
但我不想手动解析它。
还要注意,州列表的顺序和每个州中城市列表的顺序很重要,因为我坚持它们的显示顺序的概念。所以这也需要从表单值中保留下来。
我已经尝试过动态列表绑定的变体,例如 this 和 this。但是,为了让绑定正常工作,将 html 和添加大量(容易出错的)javascript 感觉是错误的。表单值已经存在;应该只是在服务器上捕获它们。
【问题讨论】:
-
你能分享你想传递给 MVC 动作的类吗?据我了解,您想传递多个状态,为此使用
for而不是foreach。 -
摆脱您永远无法使用的自定义 ModelBinder。您需要正确生成视图。一些选项请参考this answer
-
注意嵌套集合,你需要使用this plugin而不是
BeginCollectionItem -
或者如果你想在客户端做这一切 - 参考this DotNetFiddle
标签: c# asp.net-mvc model-binding custom-model-binder