【问题标题】:validation error duplication in both forms两种形式的验证错误重复
【发布时间】:2021-03-27 16:52:32
【问题描述】:

我有两个局部视图,每个都包含一个表单。当我提交其中任何一个时,验证错误在两个部分中都显示为重复项。我错过了什么,或者可能有双重的。 我必须在单独的局部视图中移动每个表单,以便为每个表单提供自己的模型。 我的主要观点:

@inject Microsoft.Extensions.Localization.IStringLocalizer Localizer
@model LoginRegisterViewModel
@{
    Layout = "_Layout_";
}

@if (Model.LoginViewModel.EnableLocalLogin)
{
    
<div class="loginRegister">
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header loginpage">
     <main class="fullHeight">
    <div class="flexValign" id="overview">
     
      <section class="section--center mdl-grid mdl-grid--no-spacing mdl-shadow--8dp roundedWrapper">

  @Html.Partial("_LoginPartial", Model.LoginViewModel)
    @Html.Partial("_RegisterPartial", Model.RegisterViewModel)

      </section>
   
      </div>
    </main>
  </div>
  </div>

} 我的部分:

    @inject Microsoft.Extensions.Localization.IStringLocalizer Localizer
    @model RegisterViewModel

<div class="mdl-card mdl-cell mdl-cell--6-col-desktop mdl-cell--6-col-tablet mdl-cell--4-col-phone darkBckrnd">

@using (Html.BeginForm("Register", "Account", FormMethod.Post))
    {
     <div class="fixedHeight mdl-grid mdl-card__supporting-text mdl-typography--text-center mdl-color-text--white">
      <div class="innerWrapper">
      
       @await Html.PartialAsync("_InputValidation")
       <input type="hidden" asp-for="RegisterReturnUrl" />
   
   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
    <input type="password" class="mdl-textfield__input" asp-for="NewPassword" autocomplete="new-password">
    <label class="mdl-textfield__label mdl-color-text--white" for="Password">@Localizer["password"]
     &ast;</label>
   </div>
   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
    <input type="password" autocomplete="off" class="mdl-textfield__input" asp-for="NewPasswordAgain">
    <label class="mdl-textfield__label mdl-color-text--white"
     for="PasswordAgain">@Localizer["repeatPassword"]
     &ast;</label>
   </div>     
  </div>
 </div>
 <div class="btnContainer">
  <button type="submit" value="register" name="button"
   class="general mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored mdl-color--cyan-500 mdl-shadow--8dp"
   id="RegisterButton">@Localizer["createAccount"]</button>
 </div>
}

另一部分:

@inject Microsoft.Extensions.Localization.IStringLocalizer Localizer
@model LoginViewModel

<div class="mdl-card mdl-cell mdl-cell--6-col-desktop mdl-cell--6-col-tablet mdl-cell--4-col-phone white">
    @using (Html.BeginForm("Login", "Account", FormMethod.Post))
    {
     <div
      class="fixedHeight mdl-grid mdl-card__supporting-text mdl-typography--text-center mdl-color-text--blue-grey-900">

  <div class="innerWrapper">
  
   <partial name="_InputValidation" />
   <input type="hidden" asp-for="ReturnUrl" />

   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
    <input class="mdl-textfield__input" type="text" asp-for="Username">
    <label class="mdl-textfield__label mdl-color-text--blue-grey-600"
     for="Username">@Localizer["userNameOrEmail"]</label>
   </div>
   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
    <input type="password" class="mdl-textfield__input" asp-for="Password" autocomplete="off">
    <label class="mdl-textfield__label mdl-color-text--blue-grey-600"
     for="Password">@Localizer["password"]</label>
   </div>
  </div>
 </div>
 <div class="btnContainer">
  <button type="submit" value="login" name="button"
   class="general mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored mdl-color--cyan-500">
   @Localizer["login"]</button>
 </div>
}

型号:

using System;
using System.Collections.Generic;
using System.Linq;

namespace IdentityServer4.Quickstart.UI
{
    public class LoginViewModel : LoginInputModel
    {
        public bool AllowRememberLogin { get; set; } = true;
        public bool EnableLocalLogin { get; set; } = true;
        public string LoginPage { get; set; } = "Login";

        public IEnumerable<ExternalProvider> ExternalProviders { get; set; } = Enumerable.Empty<ExternalProvider>();
        public IEnumerable<ExternalProvider> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));

        public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
        public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
    }
}

第二个模型

    using IdentityServer.DTOs;
        using IdentityServer.Models;
        
        namespace IdentityServer4.Quickstart.UI
        {
            public class RegisterViewModel
            {
                public string Email { get; set; }
                public string NewPassword { get; set; }
  

        public string NewPasswordAgain { get; set; }
            public string RegisterReturnUrl { get; set; }
}

然后两个模型联合起来。我已经将这两个模型合并到控制器中来覆盖它们。

using System;
using System.Collections.Generic;
using System.Linq;

namespace IdentityServer4.Quickstart.UI
{
    public class LoginRegisterViewModel
    {
        public LoginViewModel LoginViewModel { get; set; }
        public RegisterViewModel RegisterViewModel { get; set; }
    }
}

和控制器:

if (ModelState.IsValid)
            {
                var user = await _userManager.FindByNameAsync(model.Username);
                if (await _userManager.CheckPasswordAsync(user, model.Password))
                {
                    var performAdditionalCheck = true;
                    // If the user has unknown provider let him pass
                    if (user.Provider == UserAuthentificationProvider.Unknown) performAdditionalCheck = false;
                    if (context != null)
                    {
                        var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
                        if (client.Properties["forceAuthentification"] == "false") performAdditionalCheck = false;
                    }

                    if (performAdditionalCheck)
                    {
                        return await RedirectToAuth(model);
                    }
                    else
                    {
                        return await SignInUserWithModel(user, model);
                    }
                }
                await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.ClientId));
                ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
            }
            // something went wrong, show form with error
            var vm = await BuildLoginRegisterViewModelAsync(model);
            return View(vm.LoginViewModel.LoginPage, vm);
        }

【问题讨论】:

  • 请参考部分视图和视图模型的正确实现。您可以在主页中保留一个包含多个部分视图的表单,如果您面临与模型(视图模型)相关的问题,则制作一个包含 LoginViewModel 和 RegisterViewModel 的所有属性的单个 ViewModel。

标签: asp.net-mvc validation asp.net-core razor asp.net-mvc-partialview


【解决方案1】:

我有两个局部视图,每个视图都包含一个表单。当我提交其中任何一个时,验证错误在两个部分中都显示为重复。

您可以参考以下示例代码 sn-p 和这个关于"Client-side validation" 的文档,以在多个局部视图中进行有效的用户输入。

主视图

@model LoginRegisterViewModel
@{
    ViewData["Title"] = "LoginRegister";
}

<h1>LoginRegister</h1>


@await Html.PartialAsync("_LoginPartial", Model.LoginViewModel)
@await Html.PartialAsync("_RegisterPartial", Model.RegisterViewModel)

@section scripts{
    <partial name="_ValidationScriptsPartial" />
}

_LoginPartial.cshtml

@model LoginViewModel

@using (Html.BeginForm("Login", "Account", FormMethod.Post))
{
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Username" class="control-label"></label>
        <input asp-for="Username" class="form-control" />
        <span asp-validation-for="Username" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Password" class="control-label"></label>
        <input asp-for="Password" class="form-control" />
        <span asp-validation-for="Password" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input type="submit" value="login" class="btn btn-primary" />
    </div>

}

_RegisterPartial.cshtml

@model RegisterViewModel

@using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Email" class="control-label"></label>
        <input asp-for="Email" class="form-control" />
        <span asp-validation-for="Email" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="NewPassword" class="control-label"></label>
        <input asp-for="NewPassword" class="form-control" />
        <span asp-validation-for="NewPassword" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="NewPasswordAgain" class="control-label"></label>
        <input asp-for="NewPasswordAgain" class="form-control" />
        <span asp-validation-for="NewPasswordAgain" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input type="submit" value="CreateAccount" class="btn btn-primary" />
    </div>
}

测试模型类

public class LoginViewModel
{
    [Required]
    public string Username { get; set; }
    
    [DataType(DataType.Password)]
    public string Password { get; set; }
}

public class RegisterViewModel
{
    [Required]
    public string Email { get; set; }
    [Required]
    [DataType(DataType.Password)]
    public string NewPassword { get; set; }
    [Compare("NewPassword")]
    [DataType(DataType.Password)]
    public string NewPasswordAgain { get; set; }
}

测试结果

【讨论】:

  • 谢谢韩飞。 _ValidationScriptsPartial 脚本中有什么内容?
  • What's in _ValidationScriptsPartial script? Views/Shared 文件夹下的 _ValidationScriptsPartial.cshtml 将有助于添加支持 client-side validation 的 jQuery Unobtrusive Validation 脚本。
猜你喜欢
  • 2014-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-25
  • 1970-01-01
相关资源
最近更新 更多