Текущий проект:
- DotNet 4.7
- MVC 5
- Сначала в базе данных и генерируется из очень старой базы данных веб-форм (поля nchar и т. П.…не спрашивайте почему)
Так что я испытываю очень странное поведение из-за Fluent Validation.
То есть,
ненулевые Nullable поля проверяются только нана стороне сервера.Возьмите поле int
(а НЕ int?
), которое должно быть заполнено значением раскрывающегося меню, и хотя оно будет проверяться на стороне сервера, оно будет де-валидировано только на стороне клиента.Он не будет повторно проверяться на стороне клиента, если вы снова выберете недопустимое нулевое значение («Выберите выбор»).
Это поведение ограничено int
полями, которые заполняются из поля вводасписки вниз.
Все строки, даты и любые другие типы полей не могут быть проверены на стороне клиента до тех пор, пока форма не будет отправлена (после появления жадной проверки), и вообще не будут проверяться на стороне сервера.,Кроме того, все объявления .NotEmpty()
и .NotNull()
игнорируются, даже если они являются единственными в проверке для строкового поля.
У меня правильно настроен Global.asax.cs
:
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
FluentValidationModelValidatorProvider.Configure();
}
}
У меня есть правильные JS-файлы, поступающие на страницу:
<link href="/Content/bootstrap.css" rel="stylesheet"/>
<link href="/Content/bootstrap-datepicker3.css" rel="stylesheet"/>
<link href="/Content/fontawesome-all.css" rel="stylesheet"/>
<link href="/Content/style.css" rel="stylesheet"/>
<script src="/Scripts/modernizr-2.8.3.js"></script>
<script src="/Scripts/jquery-3.3.1.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="/Scripts/bootstrap.js"></script>
<script src="/Scripts/bootstrap-datepicker.js"></script>
<script src="/Scripts/popper.js"></script>
<script src="/Scripts/jquery.mask.js"></script>
<script src="/Scripts/script.js"></script>
Моя ViewModel настроена соответствующим образом:
namespace Project.Models {
using Controllers;
using FluentValidation.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Validators;
[Validator(typeof(MoreInfoValidator))]
public class MoreInfoViewModel {
[DisplayName(@"First Name")]
public string FirstName { get; set; }
[DisplayName(@"Last Name")]
public string LastName { get; set; }
[DisplayName(@"Phone Number")]
[DataType(DataType.PhoneNumber)]
public string Phone { get; set; }
[DisplayName(@"eMail Address")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DisplayName(@"Date of Birth")]
[DataType(DataType.DateTime)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Dob { get; set; } = DateTime.Now.AddYears(-16);
[DisplayName(@"Mailing Address")]
public string Address { get; set; }
[DisplayName(@"City")]
public string City { get; set; }
[DisplayName(@"Province or State")]
public string ProvState { get; set; }
[DisplayName(@"Postal Code")]
[DataType(DataType.PostalCode)]
public string Postal { get; set; }
[DisplayName(@"Country")]
public int CountryId { get; set; }
[DisplayName(@"How did you hear about us?")]
public int HowHeardId { get; set; }
[DisplayName(@"Training Site")]
public int TrainingSiteId { get; set; }
[DisplayName(@"Comments")]
public string Comments { get; set; }
public IEnumerable<SelectListItem> HowHeardList = ListController.HowHeardList();
public IEnumerable<SelectListItem> CountryList = ListController.CountryList();
public IEnumerable<SelectListItem> TrainingSiteList = ListController.TrainingSiteList();
}
}
У меня правильно настроены мои валидаторы:
namespace Project.Validators {
using FluentValidation;
using Models;
public class MoreInfoValidator : AbstractValidator<MoreInfoViewModel> {
public MoreInfoValidator() {
RuleFor(x => x.FirstName)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage("You must provide a first name of some kind.")
.MinimumLength(2).WithMessage(@"A first name must be at least two characters or longer.");
RuleFor(x => x.LastName)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage(@"You must provide a last name of some kind.")
.MinimumLength(2).WithMessage(@"A last name must be at least two characters or longer.");
RuleFor(x => x.Email.Trim())
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage(@"Please provide an eMail address to act as the login username.")
.EmailAddress().WithMessage(@"Please provide a valid eMail address to act as the login username.");
RuleFor(x => x.Phone)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage("Please enter a valid 10-digit phone number.")
.Length(12, 12).WithMessage("Phone number must be in the form of “123-456-7890”")
.Matches(@"^\d{3}-\d{3}-\d{4}$").WithMessage("Phone number must be a valid 10-digit phone number with dashes, in the form of “123-456-7890”");
RuleFor(x => x.Address)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage("Please provide your street address.")
.MinimumLength(6).WithMessage("Addresses should be at least 6 characters long.");
RuleFor(x => x.City)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage("Please provide your city.")
.MinimumLength(2).WithMessage("City names should be at least 2 characters long.");
RuleFor(x => x.ProvState)
.Cascade(CascadeMode.StopOnFirstFailure)
.NotEmpty().WithMessage("Please provide your province or state.")
.Length(2).WithMessage("Please provide the 2-character code for your province or state.");
RuleFor(x => x.CountryId)
.NotEmpty().WithMessage("Please choose your country.");
RuleFor(x => x.HowHeardId)
.NotEmpty().WithMessage("How did you hear of us?");
RuleFor(x => x.TrainingSiteId)
.NotEmpty().WithMessage("Please choose a desired training site.");
}
}
}
Мои формы построены правильно:
@model Project.Models.MoreInfoViewModel
@{
ViewBag.Title = "More Info";
}
<h1>@ViewBag.Title</h1>
<p><span class="requiredcolor">These fields</span> are required.</p>
@using(Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationMessage("", new { @class = "alert" })
<div class="row">
<div class="form-group col-md-6">
@Html.LabelFor(x => x.FirstName, new { @class = "control-label required" })@Html.EditorFor(x => x.FirstName, new { htmlAttributes = new { @class = "form-control required", maxlength = 100 } })
@Html.ValidationMessageFor(x => x.FirstName)
</div>
<div class="form-group col-md-6">
@Html.LabelFor(x => x.LastName, new { @class = "control-label required" })@Html.EditorFor(x => x.LastName, new { htmlAttributes = new { @class = "form-control required", maxlength = 100 } })
@Html.ValidationMessageFor(x => x.LastName)
</div>
</div>
<div class="row">
<div class="form-group col-md-4">
@Html.LabelFor(x => x.Phone, new { @class = "control-label required" })@Html.EditorFor(x => x.Phone, new { htmlAttributes = new { @class = "form-control required phone", maxlength = 12 } })
@Html.ValidationMessageFor(x => x.Phone)
</div>
<div class="form-group col-md-4">
@Html.LabelFor(x => x.Email, new { @class = "control-label required" })@Html.EditorFor(x => x.Email, new { htmlAttributes = new { @class = "form-control required", maxlength = 75 } })
@Html.ValidationMessageFor(x => x.Email)
</div>
<div class="form-group col-md-4">
@Html.LabelFor(x => x.Dob, new { @class = "control-label required" })@Html.EditorFor(x => x.Dob, new { htmlAttributes = new { @class = "form-control required datepicker" } })
@Html.ValidationMessageFor(x => x.Dob)
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
@Html.LabelFor(x => x.Address, new { @class = "control-label required" })@Html.EditorFor(x => x.Address, new { htmlAttributes = new { @class = "form-control required", maxlength = 150 } })
@Html.ValidationMessageFor(x => x.Address)
</div>
<div class="form-group col-md-6">
@Html.LabelFor(x => x.City, new { @class = "control-label required" })@Html.EditorFor(x => x.City, new { htmlAttributes = new { @class = "form-control required", maxlength = 50 } })
@Html.ValidationMessageFor(x => x.City)
</div>
</div>
<div class="row">
<div class="form-group col-md-4">
@Html.LabelFor(x => x.ProvState, new { @class = "control-label required" })@Html.EditorFor(x => x.ProvState, new { htmlAttributes = new { @class = "form-control required", maxlength = 2 } })
@Html.ValidationMessageFor(x => x.ProvState)
</div>
<div class="form-group col-md-4">
@Html.LabelFor(x => x.CountryId, new { @class = "control-label required" })@Html.DropDownListFor(x => x.CountryId, Model.CountryList, "« ‹ Select › »", new { @class = "form-control required" })
@Html.ValidationMessageFor(x => x.CountryId)
</div>
<div class="form-group col-md-4">
@Html.LabelFor(x => x.Postal, new { @class = "control-label" })@Html.EditorFor(x => x.Postal, new { htmlAttributes = new { @class = "form-control postalcode", maxlength = 7 } })
@Html.ValidationMessageFor(x => x.Postal)
</div>
</div>
<div class="row">
<div class="form-group col-md-4">
@Html.LabelFor(x => x.HowHeardId, new { @class = "control-label required" })@Html.DropDownListFor(x => x.HowHeardId, Model.HowHeardList, "« ‹ Select › »", new { @class = "form-control required" })
@Html.ValidationMessageFor(x => x.HowHeardId)
</div>
<div class="form-group col-md-4">
@Html.LabelFor(x => x.TrainingSiteId, new { @class = "control-label required" })@Html.DropDownListFor(x => x.TrainingSiteId, Model.TrainingSiteList, "« ‹ Select › »", new { @class = "form-control required" })
@Html.ValidationMessageFor(x => x.TrainingSiteId)
</div>
<div class="form-group col-md-4">
@Html.LabelFor(x => x.Comments, new { @class = "control-label" })@Html.TextAreaFor(x => x.Comments, new { @class = "form-control", rows = 3 })
@Html.ValidationMessageFor(x => x.Comments)
</div>
</div>
<div class="blank-divider clearfix" style="height:30px;"></div>
<div class="row">
<div class="form-group col-md-6"> </div>
<div class="form-group col-md-6"><label class="control-label"> </label><input type="submit" value="Submit Request" alt="Submit Request" title="Submit Request" class="btn btn-default btn-success" /></div>
</div>
}
Я совершенно ошеломлен, почему Fluid Validation запускается только в крайне ограниченных обстоятельствах.
Пожалуйста, поймите, что когда это делает огонь - жадная проверка после отправки для всех остальных полей, проверка на сервере для выпадающих меню - все сообщения в точности соответствуют ожиданиям.
Однако проверка не запускаетсякак для клиентской, так и для серверной части для любого поля.Если он запускает серверную часть, он не запускает клиентскую часть.Если он запускает клиентскую сторону, он делает это только в ограниченных условиях (длина строки недостаточна и т. Д.), А затем отказывается запускать серверную сторону.
Что работает не обычная проверка и любая попытка поймать .NotEmpty()
и .NotNull()
.
РЕДАКТИРОВАТЬ:
Я не вижу, как это возможно (ничего не загружается *)1062 * из БД, только вставленной в нее), но может ли проверка строковых полей быть вызвана форматом полей базы данных?Может ли это быть проблемой nchar
?
Кроме того, я использую пользовательскую модель для сбора данных, прежде чем добавить ее в модель данных для вставки в БД, поэтому у нас есть еще большее расстояниеиз структуры БД.Теоретически я не мог понять, как такие отношения возможны до перемещения данных из ViewModel в DataModel.
EDIT 2:
.Cascade(CascadeMode.StopOnFirstFailure)
не имеет значения,проблемы возникают с или без них в классе Validator.
РЕДАКТИРОВАТЬ 3:
Позвольте мне быть более точным о жадной проверке.Во всех случаях любые проверки .NotEmpty()
и .NotNull()
по-прежнему не запускаются, поэтому простой переход от одного поля к другому (после нажатия кнопки «Отправить») не приведет к жадной проверке.Жадная проверка запускается только тогда, когда вы что-то вводите, и ее недостаточно для другой проверки (слишком короткой, недействительной электронной почты, неправильного номера телефона и т. Д.).Вот почему я пришел к своему первому редактированию (см. Выше), потому что, возможно, система не видит эти строковые поля пустыми или пустыми, даже если они есть.
РЕДАКТИРОВАТЬ 4:
Больше странностей WTF.Когда я вставляю частичную / неполную строку во что-то, что делает больше, чем просто анализ длины - например, только первую половину электронного письма, в поле электронной почты - и затем нажимаю кнопку Отправить, ВНУТРЕННЯЯ СЕРВЕРНАЯ ВАЛИДАЦИЯ НАХОДИТСЯ ДЛЯ ВСЕХ СТРОКПОЛЯ, ДАЖЕ НУЛЕВОЙ / ПУСТОЙ.
Как, серьезно, Виски.Танго.Фокстрот.
РЕДАКТИРОВАТЬ 5:
WTF x10:Редактирование 4 происходит только , если были выбраны три раскрывающихся списка .Если какое-либо из трех раскрывающихся списков все еще не выбрано, проверка на стороне сервера не срабатывает ни для одного текстового поля.
Плюс, если все три раскрывающихся списка выбраны, полная проверка с использованием .NotEmpty()
и .NotNull()
неожиданно завершаются успешно во всех текстовых полях, включая как серверную , так и жадную проверку на стороне клиента.
Святой тамале.Это становится странным.