English 中文(简体)
Blazor DataAnnotations Compare validation conflicts with browser autocomplete
原标题:

I have a Blazor server app that has a form with two password inputs, "new password" and "confirm password". I m using DataAnnotations Compare attribute to validate that the two match. I ve also tried the CompareProperty attribute from the experimental NuGet package Microsoft.AspNetCore.Components.DataAnnotations.Validation, but the behavior is the same.

The sample page below is able to reproduce the error in Chrome and Edge. Testing steps:

  1. Generate a random password using the browser password generation tool (in Chrome, right click -> Suggest password...). Both password fields should be auto filled in with the generated password.
  2. In the "new password" field, add one character at the end. This updates the value in "confirm password" as well, so the values do match, but a validation failure is triggered.
  3. To remove the validation failure, delete the last character in "confirm password", tab out of the field, then go back to the field and add the same character back in. This time, the validation is successful.

Just from personal experience, I sometimes add extra characters to the end of passwords suggested by Chrome, mostly to satisfy complexity requirements the generated password didn t meet. So this form behavior will be problematic if my users do the same. Anyone know what s going on here and how to solve it?

Index.razor:

@page "/"
@using System.ComponentModel.DataAnnotations;

<PageTitle>Index</PageTitle>

<EditForm name="passwordForm" Model="MyModel" OnSubmit="@SubmitPasswordForm">
    <DataAnnotationsValidator />
    <div class="form-group">
        <label for="NewPassword">New Password</label>
        <InputText @bind-Value="MyModel.NewPassword" class="form-control" id="NewPassword" name="NewPassword" type="password" autocomplete="new-password"></InputText>
        <ValidationMessage For="@(() => MyModel.NewPassword)" />
    </div>
    <div class="form-group">
        <label for="ConfirmNewPassword">Confirm Password</label>
        <InputText @bind-Value="MyModel.ConfirmPassword" class="form-control" id="ConfirmNewPassword" name="ConfirmNewPassword" type="password" autocomplete="new-password"></InputText>
        <ValidationMessage For="@(() => MyModel.ConfirmPassword)" />
    </div>

    <input id="submitResetPassword" type="submit" class="btn btn-default" value="Reset Password" />
</EditForm>

@code {
    private Model MyModel { get; set; } = new();

    private void SubmitPasswordForm()
    {
        // Do something with the password
    }

    private class Model
    {
        [Required]
        public string? NewPassword { get; set; }
        [Required]
        [Compare(nameof(NewPassword))]
        // or [CompareProperty(nameof(NewPassword))]
        public string? ConfirmPassword { get; set; }
    }
}
问题回答

You may need trigger validate onblur as below:

    @using System.ComponentModel.DataAnnotations;

<EditForm name="passwordForm" EditContext="@EC" OnSubmit="@SubmitPasswordForm">
    <DataAnnotationsValidator />
    <div class="form-group">
        <label for="NewPassword">New Password</label>
        <InputText @bind-Value="MyModel.NewPassword" class="form-control" id="NewPassword" name="NewPassword" type="password" autocomplete="new-password" onblur="@OnblurHandler"></InputText>
        <ValidationMessage For="@(() => MyModel.NewPassword)" />
    </div>
    <div class="form-group">
        <label for="ConfirmNewPassword">Confirm Password</label>
        <InputText @bind-Value="MyModel.ConfirmPassword" class="form-control" id="ConfirmNewPassword" name="ConfirmNewPassword" type="password" autocomplete="new-password" ></InputText>
        <ValidationMessage For="@(() => MyModel.ConfirmPassword)" />
    </div>

    <input id="submitResetPassword" type="submit" class="btn btn-default" value="Reset Password" />
</EditForm>

@code {
    private Model MyModel { get; set; } = new();

    private void SubmitPasswordForm()
    {
        // Do something with the password
    }
    protected override void OnInitialized()
    {
        EC = new EditContext(MyModel);
        base.OnInitialized();
    }

    private void OnblurHandler()
    {
        EC.Validate(); // manually trigger the validation here
    }

    private EditContext EC { get; set; }

    private class Model
    {
        [Required]
        public string? NewPassword { get; set; }
        [Required]
        [Compare(nameof(NewPassword))]
        // or [CompareProperty(nameof(NewPassword))]
        public string? ConfirmPassword { get; set; }
    }
}

Result: enter image description here





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签