Страница Razor - форма с несколькими обработчиками / проверкой на стороне сервера без заполнения тегов asp-validation-for при отправке - PullRequest
0 голосов
/ 28 октября 2019

У меня есть форма, которая используется для отправки одного из двух объектов. Используя Fluent Validation, каждый объект имеет отдельные правила проверки, которые обрабатываются путем выполнения ModelState.Clear () и TryValidateModel (objectName) в соответствующем обработчике записи. Проверка работает правильно, но отображается только в теге asp-validation-summary, а не в тегах asp-validation-for, которые сопровождают каждое поле. У кого-нибудь есть идеи, как обойти это? (Использование 2 форм не вариант.) Вот код, который можно использовать для репликации проблемы:

TestValidation.cshtml:

@page
@model MyApp.Namespace.TestValidationModel
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>TestValidation</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
    <h1>Test Validation</h1>
    <form method="post">
        <div asp-validation-summary="All" class="text-danger"></div>
        <div class="row">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">ObjectToValidate1 - posts with empty handler, validates server side.</div>
                    <div class="card-body">
                        <div class="form-group">
                            <label class="control-label">Borrower Type</label>
                            <select asp-for="ObjectToValidate1.Item" asp-items="@Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
                            <span asp-validation-for="ObjectToValidate1.Item" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label class="control-label"></label>
                            <label asp-for="ObjectToValidate1.RequiredString" class="control-label businessName"></label>
                            <input asp-for="ObjectToValidate1.RequiredString" class="form-control autofocus" />
                            <span asp-validation-for="ObjectToValidate1.RequiredString" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="control-label"></label>
                            <input asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="form-control" />
                            <span asp-validation-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <button type="submit" class="btn btn-success" asp-page-handler="">Submit</button>
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">ObjectToValidate2 - posts with handler of TestValidation, validates serverside</div>
                    <div class="card-body">
                        @*<div class="form-group">
                            <label class="control-label">Borrower Type</label>
                            <select asp-for="ObjectToValidate2.Item" asp-items="@Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
                            <span asp-validation-for="ObjectToValidate2.Item" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label class="control-label"></label>
                            <label asp-for="ObjectToValidate2.RequiredString" class="control-label businessName"></label>
                            <input asp-for="ObjectToValidate2.RequiredString" class="form-control autofocus" />
                            <span asp-validation-for="ObjectToValidate2.RequiredString" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="control-label"></label>
                            <input asp-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="form-control" />
                            <span asp-validation-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <button type="submit" class="btn btn-success" asp-page-handler="TestValidation">Submit</button>
                        </div>*@
                    </div>
                </div>
            </div>
        </div>
    </form>

    @*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>*@
</body>
</html>

TestValidation.cshtml.cs

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MyApp.Namespace
{
    public class TestValidationModel : PageModel
    {
        [BindProperty]
        public InputValues ObjectToValidate1 { get; set; }
        [BindProperty]
        public InputValues ObjectToValidate2 { get; set; }
        public List<SelectListItem> DDLItems { get; set; }

        public void OnGet()
        {
            ObjectToValidate1 = new InputValues();
            ObjectToValidate2 = new InputValues();
            InitializeDDLItems();
        }

        public ActionResult OnPost()
        {
            ModelState.Clear();
            TryValidateModel(ObjectToValidate1);

            if (ModelState.IsValid)
            {
                // Refresh current page
                return RedirectToPage("./TestValidation");
            }
            InitializeDDLItems();
            return Page();
        }
        public ActionResult OnPostTestValidation()
        {
            ModelState.Clear();
            TryValidateModel(ObjectToValidate2);

            if (ModelState.IsValid)
            {
                // Refresh current page
                return RedirectToPage("./TestValidation");
            }
            InitializeDDLItems();
            return Page();
        }
        private void InitializeDDLItems()
        {
            DDLItems = new List<SelectListItem>
            {
                new SelectListItem("-- Select an Item -- ", ""),
                new SelectListItem("Item 1", "1"),
                new SelectListItem("Item 2", "2"),
            };
        }
    }
    public class InputValues
    {
        [Display(Name ="Item")]
        public int Item { get; set; }
        [Display(Name = "Required String")]
        public string RequiredString { get; set; }
        [Display(Name = "Conditional Required String")]
        public string RequiredStringIfItem1Selected { get; set; }
    }
    public class Validator : AbstractValidator<InputValues>
    {
        public Validator()
        {
            RuleFor(i => i.Item).NotEmpty().WithMessage("Item is required");
            RuleFor(i => i.RequiredString).NotEmpty().WithMessage("Required String is required");
            When(i => i.Item.Equals(1), () =>
            {
                RuleFor(e => e.RequiredStringIfItem1Selected).NotEmpty().WithMessage("Conditional Required String is required if Item 1 selected");
            });
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 01 ноября 2019

Ответ: не используйте ModelState.Clear (). Это очищает ModelState до такой степени, что даже после запуска TryValidateModel (имя модели) у получающегося ModelState не будет достаточно для отображения тегов проверки. Итак, вот упрощенная версия моего предыдущего примера, с закомментированной ModelState.Clear () и Run ModelState.ClearValidationState () для каждого объекта, который мы хотим проверить на стороне сервера. Для запуска моего исходного кода выше, я бы ClearValidationState для полей, которые не имеют отношения к текущему методу OnPost.

Страница Razor:

@page
@model MyApp.Namespace.TestValidation2Model
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>TestValidation</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
    <h1>Test Validation</h1>
    <form method="post">
        @*<div asp-validation-summary="All" class="text-danger"></div>*@
        <div class="row">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">ObjectToValidate1 - posts with empty handler, validates server side.</div>
                    <div class="card-body">
                        <div class="form-group">
                            <label class="control-label">Borrower Type</label>
                            <select asp-for="ObjectToValidate1.Item" asp-items="@Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
                            <span asp-validation-for="ObjectToValidate1.Item" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label class="control-label"></label>
                            <label asp-for="ObjectToValidate1.RequiredString" class="control-label businessName"></label>
                            <input asp-for="ObjectToValidate1.RequiredString" class="form-control autofocus" />
                            <span asp-validation-for="ObjectToValidate1.RequiredString" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="control-label"></label>
                            <input asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="form-control" />
                            <span asp-validation-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <button type="submit" class="btn btn-success" asp-page-handler="">Submit</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </form>

    @*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>*@
</body>
</html>

PageModel:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MyApp.Namespace
{
    public class TestValidation2Model : PageModel
    {
        [BindProperty]
        public InputValues2 ObjectToValidate1 { get; set; }
        public List<SelectListItem> DDLItems { get; set; }

        public void OnGet()
        {
            ObjectToValidate1 = new InputValues2();
            InitializeDDLItems();
        }

        public ActionResult OnPost()
        {
            ModelState.ClearValidationState(nameof(ObjectToValidate1.RequiredString));//.Clear();
            ModelState.ClearValidationState(nameof(ObjectToValidate1.Item));//.Clear();
            ModelState.ClearValidationState(nameof(ObjectToValidate1.RequiredStringIfItem1Selected));
            //ModelState.Clear();
            TryValidateModel(ObjectToValidate1);
            var jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(ModelState);
            if (ModelState.IsValid)
            {
                // Refresh current page
                return RedirectToPage("./TestValidation2");
            }
            InitializeDDLItems();
            return Page();
        }
        private void InitializeDDLItems()
        {
            DDLItems = new List<SelectListItem>
            {
                new SelectListItem("-- Select an Item -- ", ""),
                new SelectListItem("Item 1", "1"),
                new SelectListItem("Item 2", "2"),
            };
        }
    }
    public class InputValues2
    {
        [Display(Name = "Item")]
        public int Item { get; set; }
        [Display(Name = "Required String")]
        public string RequiredString { get; set; }
        [Display(Name = "Conditional Required String")]
        public string RequiredStringIfItem1Selected { get; set; }
    }
    public class Validator2 : AbstractValidator<InputValues2>
    {
        public Validator2()
        {
            RuleFor(i => i.Item).NotEmpty().WithMessage("Item is required");
            RuleFor(i => i.RequiredString).NotEmpty().WithMessage("Required String is required");
            When(i => i.Item.Equals(1), () =>
            {
                RuleFor(e => e.RequiredStringIfItem1Selected).NotEmpty().WithMessage("Conditional Required String is required if Item 1 selected");
            });
        }

    }
}

0 голосов
/ 29 октября 2019

Вам необходимо добавить следующие файлы javascript, которые поддерживают проверку на стороне клиента:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryvalidate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>

Сценарий ненавязчивой проверки jQuery - это настраиваемая интерфейсная библиотека Microsoft, основанная на популярном jQuery Validateплагин. Без ненавязчивой проверки jQuery вам пришлось бы кодировать одну и ту же логику проверки в двух местах: один раз в атрибутах проверки на стороне сервера в свойствах модели, а затем снова в скриптах на стороне клиента.

Ссылка:https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.0#client-side-validation

...