Blazor中的表单验证方法摘要


总览

在Blazor中介绍表单验证方法。
我将以以下登录表单为例。

ファイル名

本文的演示(从菜单中选择"表单")
源代码

假设

.NET Core SDK 3.1.100-preview3-014645
Microsoft.AspNetCore.Blazor 3.1.0-preview2.19528.8
Visual Studio 2019

我正在使用Web程序集版本(客户端版本)。

此外,该示例还将MatBlazor用作UI元素。
有关详情,请参见下文。
https://qiita.com/nobu17/items/ecf2121f7bbb6bc5294b

如果不使用MatBlazor,则将其替换为常规Form元素。
MatTextField→InputText或输入
MatButton→按钮(类型="提交")

基本版

我们将实现并解释最常用的基本模式。

创建输入模型

首先,定义要绑定到输入屏幕的类。
然后为每个项目添加一个验证定义。
用属性表示验证与ASP.NET MVC等的相同方法,因此我认为.NET开发人员对此很熟悉。

LoginData.cs

1
2
3
4
5
6
7
8
9
10
    public class LoginData
    {
        [Required(ErrorMessage = "ユーザIDを入力してください。")]
        [StringLength(16, ErrorMessage = "ユーザIDが長すぎます。")]
        public string UserID { get; set; }

        [Required(ErrorMessage = "パスワードを入力してください。")]
        [StringLength(32, ErrorMessage = "パスワードが長すぎます。")]
        public string Password { get; set; }
    }

之后创建代码

接下来,创建一个将LoginData作为成员的类,该类绑定到View。
这次,我将在后面的代码中分别描述razor组件和C#代码。
有关隐藏代码的更多信息,请参见下文。
https://qiita.com/nobu17/items/b7dc78db7beb1d833dc8

Form1ViewModel.cs

1
2
3
4
5
6
7
8
9
    public class Form1ViewModel : ComponentBase
    {
        public LoginData LoginData { get; set; } = new LoginData();

        public void Submit()
        {
            // do something
        }
    }

查看定义

创建一个屏幕以绑定后台代码。

Form1.razor

1
2
3
4
5
6
7
8
9
10
11
12
13
@inherits MatTest.Models.Form.Form1ViewModel
<EditForm Model="@LoginData" OnValidSubmit="@Submit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <MatTextField FullWidth="true" Label="UserID" @bind-Value="@LoginData.UserID"></MatTextField>
    <ValidationMessage For="@(() => LoginData.UserID)" />

    <MatTextField FullWidth="true" Label="Password" @bind-Value="@LoginData.Password" Type="password"></MatTextField>
    <ValidationMessage For="@(() => LoginData.Password)" />

    <MatButton Label="Login" Outlined="true" Type="submit">
</EditForm>

编辑表格

使用此标签将您在表单中输入的元素括起来。
-将您在表单中输入的属性绑定到"模型"。
-当输入正常时,将确认处理方法绑定到OnValidSubmit。
另外,如果要检测由于无效输入而导致按下确认按钮的情况,可以通过绑定OnInvalidSubmit事件来检测到它。

DataAnnotationsValidator

在较早验证分配给输入数据类的属性(必填等)时描述。

验证摘要

显示验证期间发生的错误的详细信息。

vali_1.png

验证消息

ValidationSummary显示所有错误详细信息,因此
当您要显示每个输入项的单独验证时,请使用此选项。
在For中使用lambda指定属性。

vali_2.png

2019/12/22附录

OnSubmit

根据验证结果,执行OnValidSubmit或OnInvalidSubmit,但是通过使用OnSubmit,可以注册始终在提交时执行的过程。
您可以使用作为参数传递的EditContext来执行验证或实现自己的验证过程。

Form1.razor

1
2
3
4
@inherits MatTest.Models.Form.Form1ViewModel
<EditForm Model="@LoginData" OnSubmit="@Submit">
 //略
</EditForm>

Form1ViewModel.cs

1
2
3
4
5
6
7
8
    public class Form1ViewModel : ComponentBase
    {
        public void Submit(EditContext editContext)
        {
            // 検証を実施して結果を取得
            bool isValid = editContext.Validate();
        }
    }

自定义验证属性

如果要创建自己的验证属性,请扩展ValidationAttribute类。
这是.NET中特定于Blazor的一种更常用的技术。

CustomeValidationAttribute.cs

1
2
3
4
5
6
7
8
9
10
11
12
    public class CustomeValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var str = value as string;
            if (str != null && string.IsNullOrWhiteSpace(str))
            {
                return new ValidationResult("空白は無効です。", new[] { validationContext.MemberName });
            }
            return ValidationResult.Success;
        }
    }

覆盖

IsValid以验证目标对象。

  • 如果正常,则返回ValidationResult.Success
  • 如果输入错误,则将错误消息和validationContext.MemberName放入ValidationResult中并返回

发生输入错误时,将显示错误消息,如下所示。

cus.PNG

进阶版

基于基础知识,我们将介绍各种情况。

嵌套类

的验证

DataAnnotationsValidator不适用于嵌套对象。
当前它仍然是预览版本,但是您可以按照以下步骤进行操作。

新增模块

从Nuget添加以下模块。

Microsoft.AspNetCore.Blazor.DataAnnotations.Validation

属性添加

将ValidateComplexType属性授予嵌套类的属性。

NestedData.cs

1
2
3
4
5
6
    public class NestedData
    {
        [Required]
        [ValidateComplexType]
        public LoginData LoginData { get; set; } = new LoginData();
    }

Varidata更改

使用ObjectGraphDataAnnotationsValidator代替DataAnnotationsValidator。

Form2.razor

1
2
3
4
5
6
7
8
9
@inherits MatTest.Models.Form.Form2ViewModel
<EditForm Model="@NestedData" OnValidSubmit="@Submit">
    <ObjectGraphDataAnnotationsValidator />
    <ValidationSummary />

    <MatTextField FullWidth="true" Label="UserID" @bind-Value="@NestedData.LoginData.UserID"></MatTextField>
    <MatTextField FullWidth="true" Label="Password" @bind-Value="@NestedData.LoginData.Password" Type="password"></MatTextField>
    <MatButton Label="Login" Outlined="true" Type="submit">
</EditForm>

使用OSS模块进行自定义验证(流式验证)

您可以使用OSS提供的功能创建除属性验证之外的其他验证。
由于介绍了使.NET验证库(例如FluentValidation与Blazor兼容)的方法,因此我们将在此基础上实现该方法。

https://chrissainty.com/using-fluentvalidation-for-forms-validation-in-razor-components/

套餐介绍

从Nuget安装Fluent验证。

创建Varidata

根据Fluent Validation方法创建一个验证类。

LoginDataValidator.cs

1
2
3
4
5
6
7
8
9
10
    public class LoginDataValidator : AbstractValidator<LoginData>
    {
        public LoginDataValidator()
        {
            RuleFor(p => p.UserID).NotEmpty().WithMessage("ログインIDを入力してください。");
            RuleFor(p => p.UserID).MaximumLength(10).WithMessage("ログインIDは10文字まで入力してください。");
            RuleFor(p => p.Password).NotEmpty().WithMessage("パスワードを入力してください。");
            RuleFor(p => p.Password).MaximumLength(10).WithMessage("パスワードは10文字まで入力してください。");
        }
    }

创建一个继承AbstractValidator的类。
(指定要验证泛型的类)
使用构造函数中的RuleFor Lambda对每个成员实施验证。

创建组件

由于无法像在Blazor中那样使用创建的验证数据,因此请创建一个组件以支持Blazor方面的验证。
Blazor提供了诸如EditContext之类的验证机制,并且Fluent Validation的验证是在该机制内执行的。
我将省略EditContext的详细信息,但是以下内容将有所帮助。
https://gunnarpeipman.com/blazor-form-validation/

Publisher(由于自发布以来某些API规范已更改,因此我们正在处理。将AddRange更改为Add。Reference)。

FluentValidationValidator.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    public class FluentValidationValidator : ComponentBase
    {
        [CascadingParameter] EditContext CurrentEditContext { get; set; }

        protected override void OnInitialized()
        {
            if (CurrentEditContext == null)
            {
                throw new InvalidOperationException($"{nameof(FluentValidationValidator)} requires a cascading " +
                    $"parameter of type {nameof(EditContext)}. For example, you can use {nameof(FluentValidationValidator)} " +
                    $"inside an {nameof(EditForm)}.");
            }

            CurrentEditContext.AddFluentValidation();
        }
    }

    public static class EditContextFluentValidationExtensions
    {
        public static EditContext AddFluentValidation(this EditContext editContext)
        {
            if (editContext == null)
            {
                throw new ArgumentNullException(nameof(editContext));
            }

            var messages = new ValidationMessageStore(editContext);

            editContext.OnValidationRequested +=
                (sender, eventArgs) => ValidateModel((EditContext)sender, messages);

            editContext.OnFieldChanged +=
                (sender, eventArgs) => ValidateField(editContext, messages, eventArgs.FieldIdentifier);

            return editContext;
        }

        private static void ValidateModel(EditContext editContext, ValidationMessageStore messages)
        {
            var validator = GetValidatorForModel(editContext.Model);
            var validationResults = validator.Validate(editContext.Model);

            messages.Clear();
            foreach (var validationResult in validationResults.Errors)
            {
                messages.Add(editContext.Field(validationResult.PropertyName), validationResult.ErrorMessage);
            }

            editContext.NotifyValidationStateChanged();
        }

        private static void ValidateField(EditContext editContext, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier)
        {
            var properties = new[] { fieldIdentifier.FieldName };
            var context = new ValidationContext(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));

            var validator = GetValidatorForModel(fieldIdentifier.Model);
            var validationResults = validator.Validate(context);

            messages.Clear(fieldIdentifier);
            // APIの仕様変更のため、Addに変更
            //messages.AddRange(fieldIdentifier, validationResults.Errors.Select(error => error.ErrorMessage));
            messages.Add(fieldIdentifier, validationResults.Errors.Select(error => error.ErrorMessage));

            editContext.NotifyValidationStateChanged();
        }

        private static IValidator GetValidatorForModel(object model)
        {
            var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(model.GetType());
            var modelValidatorType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.IsSubclassOf(abstractValidatorType));
            var modelValidatorInstance = (IValidator)Activator.CreateInstance(modelValidatorType);

            return modelValidatorInstance;
        }
    }

实施

放置创建的验证器组件(FluentValidationValidator)。

Form3.razor

1
2
3
4
5
6
7
<EditForm Model="@LoginData" OnValidSubmit="@HandleValidSubmit" class="mat-layout-grid-cell mat-layout-grid-cell-span-12">
    <FluentValidationValidator />
    <ValidationSummary />
    <MatTextField FullWidth="true" Label="UserID" @bind-Value="@LoginData.UserID"></MatTextField>
    <MatTextField FullWidth="true" Label="Password" @bind-Value="@LoginData.Password" Type="password"></MatTextField>
    <MatButton Label="Login" Outlined="true" Type="submit"></MatButton>
</EditForm>

概要

我总结了Blazor中的表单验证方法。
由于验证方法遵循常规的.NET方法,因此许多人可能对此很熟悉。

Blazor的其他文章

我写了一些有关Blazor的文章,因此,如果您愿意,请看一下。

  • 尝试使用MatBlazor,这是Blazor的UI框架
  • 更改Blazor的初始加载屏幕(正在加载)
  • 未使用Blazor登录时重定向到登录页面
  • 使用Blazor在代码隐藏中分离逻辑和视图
  • 尝试使用Radzen.Blazor,这是Blazor的UI框架
  • 在GitHub Pages上发布使用Blazor创建的网站
  • 在Firebase上发布使用Blazor创建的网站

参考资料

https://docs.microsoft.com/ja-jp/aspnet/core/blazor/forms-validation?view=aspnetcore-3.0
https://gunnarpeipman.com/blazor-form-validation/
https://chrissainty.com/using-fluentvalidation-for-forms-validation-in-razor-components/
https://blazor-university.com/forms/writing-custom-validation/
http://blazorhelpwebsite.com/Blog/tabid/61/EntryId/4337/Blazor-Forms-and-Validation.aspx
https://remibou.github.io/Client-side-validation-with-Blazor-and-Data-Annotations/
https://dzone.com/articles/blazor-form-validation
https://itnext.io/blazor-forms-and-validation-418173350435