一个视图中的多个模型 - 使用 ViewModel 登录和注册 - 带有字段和 ModelState 验证

问题描述

我有一个 MVC 5 应用程序,它在一个视图中使用多个模型进行登录注册。在 Google 和 Stack Overflow 上搜索会返回许多关于如何实现这一点的示例。这个例子部分有效。但是,我在验证方面遇到了问题,似乎找不到明确的示例。如果我点击登录表单而不在字段中输入任何值,我总是得到一个空引用,我的 ModelState 总是有效的!

我的登录模式:

public class LoginModel
{
    [required(ErrorMessage = "Please enter a valid username.")]
    public string txtUsername { get; set; }

    [required(ErrorMessage = "The password field is required.")]
    public string txtPassword { get; set; }
}

我的注册模式:

public class RegisterModel
{
    [required(ErrorMessage = "Please enter a first name.")]
    public string txtFirstName { get; set; }

    [required(ErrorMessage = "Please enter a last name.")]
    public string txtLastName { get; set; }
}

我的视图模型:

public class viewmodelVM
{
    public LoginModel loginModel { get; set; }
    public RegisterModel registerModel { get; set; }

}

我的家庭控制器提交和注册操作:

[HttpPost]
public ActionResult LoginSubmit(viewmodelVM vm)
{
    if (ModelState.IsValid)
    {
        //perform other logic. 
        return View("Index");
    }
    else
    {
        return View("Login",vm.loginModel);
    }
}
[HttpPost]
public ActionResult RegisterSubmit(viewmodelVM vm)
{
    if (ModelState.IsValid)
    { 
        return View("Index");
    }
    else
    {
        return View("Login",vm);
    }
}

我的观点:

@model MVCTemplate.viewmodel.viewmodelVM

@{
    ViewBag.Title = "Login & Register";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="col-lg-10 mx-auto">
    @using (Html.BeginForm("LoginSubmit","Home",FormMethod.Post))
    {
        <div id="loginForm" class="p-5">
            <div class="form-group">
                @Html.Label("Username",new { @class = "labeltiny" })
                @Html.TextBox("txtUserName","",new { @class = "form-control",@PlaceHolder = "Username" })
                @Html.ValidationMessageFor(x => x.loginModel.txtUsername,new { @class = "text-danger labeltiny" })
            </div>
            <div class="form-group">
                @Html.Label("Password",new { @class = "labeltiny" })
                @Html.Password("txtPassword",@PlaceHolder = "Password" })
                @Html.ValidationMessageFor(x => x.loginModel.txtPassword,new { @class = "text-danger labeltiny" })
            </div>
            <div class="form-group">
                <div class="row">
                    <div class="col-md-12 text-center">
                        <input type="submit" name="btnLogin" id="btnLogin" tabindex="4" class="form-control btn btn-login" value="Log In" onclick="sweetalertSpinner()">
                    </div>
                </div>
            </div>
        </div>
    }

    @using (Html.BeginForm("RegisterSubmit",FormMethod.Post))
    {
        <div id="registerForm" class="p-5" style="display: none">
            <div class="form-group">
                @Html.Label("First Name",new { @class = "labeltiny" })
                @Html.TextBox("txtFirstName",@PlaceHolder = "Fist Name" })
                @Html.ValidationMessageFor(x => x.registerModel.txtFirstName,new { @class = "text-danger labeltiny" })
            </div>
            <div class="form-group">
                @Html.Label("Last Name",new { @class = "labeltiny" })
                @Html.Password("txtLastName",@PlaceHolder = "Last Name" })
                @Html.ValidationMessageFor(x => x.registerModel.txtLastName,new { @class = "text-danger labeltiny" })
            </div>
            <div class="form-group">
                <div class="row">
                    <div class="col-md-12 text-center">
                        <input type="submit" name="btnRegister" id="btnRegister" tabindex="4" class="form-control btn btn-login" value="Register" onclick="sweetalertSpinner()">
                    </div>
                </div>
            </div>
        </div>
    }
</div>

解决方法

正如 docs 所说:“模型状态代表来自两个子系统的错误:模型绑定和模型验证。”

ModelState 将仅代表一个模型,因为您正在定义视图模型

public class ViewModelVM
{
    public LoginModel loginModel { get; set; }
    public RegisterModel registerModel { get; set; }

}

然后模型绑定和模型验证将基于 ViewModelVM 的属性而不是它的对象内的属性,这就是为什么你会得到 ModelState.IsValid == true

我认为您可以使用的是每个模型的手动验证,here您可以找到一种方法。

也许您可以创建一个类并从您想要的两个模型继承,但我不确定这是否可行。如果您尝试过,请告诉我们。

,

我找到了解决方案。 将以下内容添加到 ViewModel

public class ViewModelVM
{
    public LoginModel loginModel { get; set; }
    public RegisterModel registerModel { get; set; }
    public string txtUsername { get; set; }
    public string txtPassword { get; set; }
    public string txtFirstName { get; set; }
    public string txtLastName { get; set; }
}

为每个表单字段更改如下视​​图(删除对 loginModelregisterModel 的引用):

@Html.ValidationMessageFor(x => x.txtUsername,"",new { @class = "text-danger labeltiny" })

像这样在控制器中将 LoginModel 作为参数而不是 ViewModelVM 传递(对 RegisterSubmit 执行相同的操作):

[HttpPost]
public ActionResult LoginSubmit(LoginModel lm)
{
    ViewModelVM vm = new ViewModelVM();
    if (ModelState.IsValid)
    {
        //perform other logic using form fields lm.txtUsername,etc... 
        return View("Index");
    }
    else
    {
        return View("Login",vm);
    }
}