由于 PartialView 之后 ModelState 丢失,MVC 3 验证消息未显示

发布于 2025-01-07 18:01:11 字数 4532 浏览 2 评论 0原文

我有一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

猥︴琐丶欲为 2025-01-14 18:01:11

这是由于调用 new ViewDataDictionary()viewData 被清除所致。

@{Html.RenderPartial("PreRegister", Model.PreRegister, new ViewDataDictionary());}
@{Html.RenderPartial("QuickLogin", Model.QuickLogin, new ViewDataDictionary());}

根据 MSDN ViewDataDictionary 用于:

表示一个容器,用于在控制器之间传递数据
和一个视图。

我认为您对此有充分的理由,否则您不会不厌其烦地添加附加参数。

如果将其更改为:

@Html.RenderPartial("PreRegister", Model.PreRegister)
@Html.RenderPartial("QuickLogin", Model.QuickLogin)

控制器中构建的 ModelState 应该在您的视图上可用。

This is caused by the viewData being cleared out when new ViewDataDictionary() is called.

@{Html.RenderPartial("PreRegister", Model.PreRegister, new ViewDataDictionary());}
@{Html.RenderPartial("QuickLogin", Model.QuickLogin, new ViewDataDictionary());}

According to MSDN ViewDataDictionary is used for:

Represents a container that is used to pass data between a controller
and a view.

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:

@Html.RenderPartial("PreRegister", Model.PreRegister)
@Html.RenderPartial("QuickLogin", Model.QuickLogin)

the ModelState built up in the controller should be available on your views.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文