由于 PartialView 之后 ModelState 丢失,MVC 3 验证消息未显示
我有一个 MVC 3 项目,其中有 1 个视图 LoginRegister,其中包含 2 个带有登录和预注册表单的视图。问题是在错误地完成预注册表单并使用 PartialView("LoginRegister", loginRegisterViewModel) 后,由于 ModelState 丢失,验证消息不会显示。在阅读下一段之前,最好跳到代码。
调试 PartialView("LoginRegister", loginRegisterViewModel) 并进入 PreRegister 视图,查看以下 @Html.ErrorMessageFor(model => model.Email)。其中 ModelState 不包含电子邮件密钥(见下文),因此不会显示错误消息。
private static MvcHtmlString ErrorMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes)
{
string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName)) // ModelState contains no keys, e.g. Email not found and a null is returned
{
return null;
}
// code continues here to render error message
}
代码
ViewModels
LoginRegisterViewModel
namespace Site.ViewModels.Account
{
public class LoginRegisterViewModel
{
public bool IsDialog { get; set; }
public PreRegisterViewModel PreRegister { get; set; }
public QuickLoginViewModel QuickLogin { get; set; }
}
}
PreRegisterViewModel
using FluentValidation.Attributes;
using Site.ViewModels.Validators;
namespace Site.ViewModels.Account
{
[Validator(typeof(PreRegisterViewModelValidator))]
public class PreRegisterViewModel
{
public string Email { get; set; }
public string ConfirmEmail { get; set; }
}
}
视图
LoginRegister
@model Site.ViewModels.Account.LoginRegisterViewModel
@{
Layout = ViewBag.Layout;
}
<div id="loginReg" class="mod-loginReg">
<h2>Login and Registration</h2>
@{Html.RenderPartial("PreRegister", Model.PreRegister, new ViewDataDictionary());}
@{Html.RenderPartial("QuickLogin", Model.QuickLogin, new ViewDataDictionary());}
</div>
PreRegister
@using Site.Classes.MVC.Extensions;
@model Site.ViewModels.Account.PreRegisterViewModel
@using (Html.BeginForm("PreRegister", "Account", FormMethod.Post, new { @class = "form-errorContainer" }))
{
<div class="mod-loginReg-register">
<h3>Register</h3>
<p>Please enter your email address to register.<br /><br /></p>
<div class="mod-loginReg-form">
<div class="field-item">
@Html.LabelFor(model => model.Email)
@Html.TextBoxFor(model => model.Email, new { @maxlength = "255", @class = "Textbox required email" })
@Html.ErrorMessageFor(model => model.Email)
</div>
<div class="field-item">
@Html.LabelFor(model => model.ConfirmEmail)
@Html.TextBoxFor(model => model.ConfirmEmail, new { @maxlength = "255", @class = "Textbox required email" })
@Html.ErrorMessageFor(model => model.ConfirmEmail)
</div>
<div class="button-group">
@Html.Button("Register", "Register")
</div>
</div>
</div>
}
<强>帐户控制器
[RequireHttps]
public ActionResult LoginRegister(int showDialog)
{
var loginRegisterViewModel = new LoginRegisterViewModel();
if (showDialog == 1)
{
ViewBag.Layout = "~/layouts/structure/dialog.cshtml";
loginRegisterViewModel.IsDialog = true;
}
else
{
ViewBag.Layout = "~/layouts/structure/2column.cshtml";
}
return PartialView(loginRegisterViewModel);
}
[RequireHttps]
public ActionResult PreRegister()
{
return PartialView();
}
[HttpPost]
[RequireHttps]
public ActionResult PreRegister(PreRegisterViewModel model)
{
if (ModelState.IsValid)
{
return PartialView("PreRegisterComplete");
}
var loginRegisterViewModel = new LoginRegisterViewModel { PreRegister = model, QuickLogin = new QuickLoginViewModel() };
return PartialView("LoginRegister", loginRegisterViewModel);
}
I have an MVC 3 project where I have 1 view LoginRegister which contains 2 views with forms for login and pre-register. The problem is after incorrectly completing the pre-register form and using PartialView("LoginRegister", loginRegisterViewModel) validation messages aren't displayed due the ModelState being lost. Before reading the next paragraph it's probably best to jump to the CODE.
Debugging the PartialView("LoginRegister", loginRegisterViewModel) and stepping into the PreRegister view to the following @Html.ErrorMessageFor(model => model.Email). Where the ModelState does not contain the Email key (see below) and hence doesn't display an error message.
private static MvcHtmlString ErrorMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes)
{
string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName)) // ModelState contains no keys, e.g. Email not found and a null is returned
{
return null;
}
// code continues here to render error message
}
CODE
ViewModels
LoginRegisterViewModel
namespace Site.ViewModels.Account
{
public class LoginRegisterViewModel
{
public bool IsDialog { get; set; }
public PreRegisterViewModel PreRegister { get; set; }
public QuickLoginViewModel QuickLogin { get; set; }
}
}
PreRegisterViewModel
using FluentValidation.Attributes;
using Site.ViewModels.Validators;
namespace Site.ViewModels.Account
{
[Validator(typeof(PreRegisterViewModelValidator))]
public class PreRegisterViewModel
{
public string Email { get; set; }
public string ConfirmEmail { get; set; }
}
}
Views
LoginRegister
@model Site.ViewModels.Account.LoginRegisterViewModel
@{
Layout = ViewBag.Layout;
}
<div id="loginReg" class="mod-loginReg">
<h2>Login and Registration</h2>
@{Html.RenderPartial("PreRegister", Model.PreRegister, new ViewDataDictionary());}
@{Html.RenderPartial("QuickLogin", Model.QuickLogin, new ViewDataDictionary());}
</div>
PreRegister
@using Site.Classes.MVC.Extensions;
@model Site.ViewModels.Account.PreRegisterViewModel
@using (Html.BeginForm("PreRegister", "Account", FormMethod.Post, new { @class = "form-errorContainer" }))
{
<div class="mod-loginReg-register">
<h3>Register</h3>
<p>Please enter your email address to register.<br /><br /></p>
<div class="mod-loginReg-form">
<div class="field-item">
@Html.LabelFor(model => model.Email)
@Html.TextBoxFor(model => model.Email, new { @maxlength = "255", @class = "Textbox required email" })
@Html.ErrorMessageFor(model => model.Email)
</div>
<div class="field-item">
@Html.LabelFor(model => model.ConfirmEmail)
@Html.TextBoxFor(model => model.ConfirmEmail, new { @maxlength = "255", @class = "Textbox required email" })
@Html.ErrorMessageFor(model => model.ConfirmEmail)
</div>
<div class="button-group">
@Html.Button("Register", "Register")
</div>
</div>
</div>
}
AccountController
[RequireHttps]
public ActionResult LoginRegister(int showDialog)
{
var loginRegisterViewModel = new LoginRegisterViewModel();
if (showDialog == 1)
{
ViewBag.Layout = "~/layouts/structure/dialog.cshtml";
loginRegisterViewModel.IsDialog = true;
}
else
{
ViewBag.Layout = "~/layouts/structure/2column.cshtml";
}
return PartialView(loginRegisterViewModel);
}
[RequireHttps]
public ActionResult PreRegister()
{
return PartialView();
}
[HttpPost]
[RequireHttps]
public ActionResult PreRegister(PreRegisterViewModel model)
{
if (ModelState.IsValid)
{
return PartialView("PreRegisterComplete");
}
var loginRegisterViewModel = new LoginRegisterViewModel { PreRegister = model, QuickLogin = new QuickLoginViewModel() };
return PartialView("LoginRegister", loginRegisterViewModel);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是由于调用
new ViewDataDictionary()
时viewData
被清除所致。根据 MSDN ViewDataDictionary 用于:
我认为您对此有充分的理由,否则您不会不厌其烦地添加附加参数。
如果将其更改为:
控制器中构建的
ModelState
应该在您的视图上可用。This is caused by the
viewData
being cleared out whennew ViewDataDictionary()
is called.According to MSDN ViewDataDictionary is used for:
I assume you have a good reason for this otherwise you would not have taken the trouble to add the additional parameter.
If you change it to:
the
ModelState
built up in the controller should be available on your views.