我在弄清楚如何在 ASP.NET MVC 中将我的视图和操作分解为可管理的块时遇到了一些麻烦,我尝试过搜索,但我仍然没有更聪明。
为了尝试了解这个特定方面,我创建了一个小测试项目,我尝试在同一页面上使用登录表单和注册表单的示例来了解情况。我的视图模型如下所示:
public class LoginOrRegisterModel
{
public LoginModel Login { get; set; }
public RegisterModel Register { get; set; }
public LoginOrRegisterModel()
{
this.Login = new LoginModel();
this.Register = new RegisterModel();
}
}
public class LoginModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
public class RegisterModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
然后我开始考虑主要行动。
public ActionResult Index()
{
return View(new LoginOrRegisterModel());
}
...并查看...
@model MvcSandbox.Models.LoginOrRegisterModel
@{
ViewBag.Title = "Index";
}
<h2>Login Or Register</h2>
@Html.Partial("Login", model: Model.Login)
@Html.Partial("Register", model: Model.Register)
...部分观点...
@model MvcSandbox.Models.LoginModel
@{
ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Login</button>
}
@model MvcSandbox.Models.RegisterModel
@{
ViewBag.Title = "Register";
}
<h2>Register</h2>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Register</button>
}
我发现我必须确保 LoginOrRegisterModel 的属性不为空,否则会出现错误:传递到字典中的模型项的类型为“MvcSandbox.Models.LoginOrRegisterModel”,但此字典需要模型项类型'MvcSandbox.Models.LoginModel'。
到目前为止还不错,虽然目前不是很有用,因为两种表单都具有相同的字段名称和 id,并且都回发到一个什么都不做的索引页面。
HTML 源代码:
<h2>Login Or Register</h2>
<h2>Login</h2>
<form action="/Membership" method="post"><div class="editor-label"><label for="UserName">UserName</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The UserName field is required." id="UserName" name="UserName" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span></div>
<div class="editor-label"><label for="Password">Password</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The Password field is required." id="Password" name="Password" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span></div>
<button>Login</button>
</form>
<h2>Register</h2>
<form action="/Membership" method="post"><div class="editor-label"><label for="UserName">UserName</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The UserName field is required." id="UserName" name="UserName" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span></div>
<div class="editor-label"><label for="Password">Password</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-required="The Password field is required." id="Password" name="Password" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span></div>
<button>Register</button>
</form>
无论如何,我当时想做的,因为我有点想为每个帖子保持逻辑独立,就是让每个表单发布到不同的操作。这就是我认为我会大错特错的地方。
本质上,如果验证失败,我想我需要做一些事情来尝试在加载页面时实际建立模型状态,但我有点明白我在下面的内容,我有点迷茫我应该采取什么方法反而。
public class MembershipController : Controller
{
//
// GET: /Membership/
public ActionResult Index()
{
if (TempData["ModelState"] != null)
ModelState.Merge((ModelStateDictionary)TempData["ModelState"]);
return View(new LoginOrRegisterModel());
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["ModelState"] = ModelState;
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["ModelState"] = ModelState;
return RedirectToAction("Index");
}
}
...与意见...
@model MvcSandbox.Models.LoginModel
@{
ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm("Login", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Login</button>
}
@model MvcSandbox.Models.RegisterModel
@{
ViewBag.Title = "Register";
}
<h2>Register</h2>
@using (Html.BeginForm("Register", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Register</button>
}
问题是因为每个模型都有同名的字段,无论提交哪个表单,验证都会显示在两个表单上,当我尝试使用 HtmlFieldPrefix 时,我似乎根本没有得到验证。
任何关于如何将我的操作和视图分解为可管理和可维护的块而不让自己对模型状态和验证感到头痛的建议将不胜感激。
更新:
我稍微改变了方法以使用似乎可以改善事情的部分操作,代码如下:
public ActionResult Index()
{
return View();
}
public ActionResult Login()
{
if (TempData["LoginModelState"] != null)
ModelState.Merge((ModelStateDictionary)TempData["LoginModelState"]);
return View(new LoginModel());
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["LoginModelState"] = ModelState;
return RedirectToAction("Index");
}
public ActionResult Register()
{
if (TempData["RegisterModelState"] != null)
ModelState.Merge((ModelStateDictionary)TempData["RegisterModelState"]);
return View(new RegisterModel());
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// do something
}
TempData["RegisterModelState"] = ModelState;
return RedirectToAction("Index");
}
我的索引视图现在是:
@{
ViewBag.Title = "Index";
}
<h2>Login Or Register</h2>
@Html.Action("Login")
@Html.Action("Register")
并登录并注册:
@model MvcSandbox.Models.LoginModel
<h2>Login</h2>
@using (Html.BeginForm("Login", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Login</button>
}
@model MvcSandbox.Models.RegisterModel
<h2>Register</h2>
@using (Html.BeginForm("Register", "Membership"))
{
@Html.ValidationSummary(true)
@Html.EditorFor(model => model)
<button>Register</button>
}
现在对我来说,这比以前的方法有优势,它实际上似乎工作得更多,并且摆脱了对 LoginOrRegisterModel 相当坦率地可怕的混乱想法的需要,这对于这样一个简单的例子来说可能很好,但很快就会变得混乱随着事情变得更加复杂,UI 被重构,可能需要大量重构模型和代码以及视图。
我真的得到了一些替换默认模型绑定器的印象,以具有某种基于描述符的模型绑定,并使控制器操作作为某种命令处理器工作,这样它就会根据发布的部分触发正确的处理程序更好,并解决下面神秘人提到的重定向带来的刷新问题。