.NET 6.0 Blazor Server表单邮箱/手机号后端唯一性验证方案咨询
.NET 6 Blazor Server 实现邮箱唯一性验证的方案
因为.NET 6 不支持向ValidationAttribute注入依赖服务,所以没法直接通过自定义EmailUniqueAttribute实现数据库查询验证,以下是几种可行的替代方案:
方案一:表单提交前手动调用验证服务
这是最直接的方式,在表单通过数据注解验证后,手动调用邮箱唯一性检查服务,根据结果决定是否继续执行注册逻辑。
步骤1:定义邮箱唯一性检查服务
先创建一个负责查询数据库的服务:
public interface IEmailUniquenessChecker { Task<bool> IsEmailUniqueAsync(string email); } public class EmailUniquenessChecker : IEmailUniquenessChecker { private readonly AppDbContext _dbContext; public EmailUniquenessChecker(AppDbContext dbContext) { _dbContext = dbContext; } public async Task<bool> IsEmailUniqueAsync(string email) { // 注意处理邮箱大小写,根据业务需求调整 return !await _dbContext.Users.AnyAsync(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase)); } }
记得在Program.cs中注册这个服务:
builder.Services.AddScoped<IEmailUniquenessChecker, EmailUniquenessChecker>();
步骤2:在Blazor组件中实现验证逻辑
在注册表单组件中注入服务,在提交时检查唯一性:
@inject IEmailUniquenessChecker EmailChecker @inject NavigationManager NavManager <EditForm Model="@RegisterModel" OnValidSubmit="@HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="mb-3"> <label class="form-label">邮箱</label> <InputText @bind-Value="RegisterModel.Email" class="form-control" /> <ValidationMessage For="@(() => RegisterModel.Email)" /> </div> <!-- 姓名、电话等其他表单字段 --> <button type="submit" class="btn btn-primary">注册</button> </EditForm> @code { private RegisterViewModel RegisterModel { get; set; } = new(); private async Task HandleValidSubmit() { // 检查邮箱唯一性 var isUnique = await EmailChecker.IsEmailUniqueAsync(RegisterModel.Email); if (!isUnique) { // 添加验证错误并刷新组件显示 var editContext = new EditContext(RegisterModel); editContext.AddModelError(nameof(RegisterModel.Email), "该邮箱已被注册,请更换其他邮箱"); StateHasChanged(); return; } // 唯一性验证通过,执行注册逻辑 // 示例:将用户信息保存到数据库 // await _userService.CreateUserAsync(RegisterModel); NavManager.NavigateTo("/login"); } public class RegisterViewModel { [Required(ErrorMessage = "请输入姓名")] public string Name { get; set; } [Required(ErrorMessage = "请输入邮箱")] [EmailAddress(ErrorMessage = "请输入有效的邮箱格式")] public string Email { get; set; } [Required(ErrorMessage = "请输入电话")] [Phone(ErrorMessage = "请输入有效的电话号码")] public string Phone { get; set; } } }
方案二:通过EditContext附加实时验证
如果需要在用户输入邮箱后实时验证(比如失去焦点时),可以通过订阅EditContext的验证事件实现:
@inject IEmailUniquenessChecker EmailChecker @implements IDisposable <EditForm Model="@RegisterModel" EditContext="@_editContext"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="mb-3"> <label class="form-label">邮箱</label> <InputText @bind-Value="RegisterModel.Email" class="form-control" /> <ValidationMessage For="@(() => RegisterModel.Email)" /> </div> <!-- 其他表单字段 --> <button type="submit" class="btn btn-primary">注册</button> </EditForm> @code { private RegisterViewModel RegisterModel { get; set; } = new(); private EditContext _editContext; private IDisposable _validationSubscription; protected override void OnInitialized() { _editContext = new EditContext(RegisterModel); // 订阅验证请求事件 _validationSubscription = _editContext.OnValidationRequested += async (sender, args) => { var emailField = _editContext.Field(nameof(RegisterModel.Email)); var email = RegisterModel.Email; if (!string.IsNullOrWhiteSpace(email)) { var isUnique = await EmailChecker.IsEmailUniqueAsync(email); if (!isUnique) { _editContext.AddModelError(emailField, "该邮箱已被注册"); } else { // 清除之前的错误(如果有) _editContext.MarkFieldAsValid(emailField); } } else { // 邮箱为空时,保留数据注解的必填验证,不处理唯一性 _editContext.MarkFieldAsValid(emailField); } }; } public void Dispose() { // 释放订阅,避免内存泄漏 _validationSubscription?.Dispose(); } // RegisterViewModel 定义同方案一 }
方案三:结合自定义验证组件
如果希望封装验证逻辑,可以创建一个独立的验证组件,复用在多个表单中:
@inject IEmailUniquenessChecker EmailChecker @implements IDisposable [Parameter] public EditContext EditContext { get; set; } [Parameter] public Expression<Func<string>> For { get; set; } @code { private FieldIdentifier _fieldIdentifier; private IDisposable _fieldSubscription; protected override void OnInitialized() { _fieldIdentifier = FieldIdentifier.Create(For); // 订阅字段值变更事件,实时验证 _fieldSubscription = EditContext.OnFieldChanged += async (sender, args) => { if (args.FieldIdentifier == _fieldIdentifier) { await ValidateEmailAsync(); } }; } private async Task ValidateEmailAsync() { var email = EditContext.Model.GetType().GetProperty(_fieldIdentifier.FieldName)?.GetValue(EditContext.Model) as string; if (!string.IsNullOrWhiteSpace(email)) { var isUnique = await EmailChecker.IsEmailUniqueAsync(email); if (!isUnique) { EditContext.AddModelError(_fieldIdentifier, "该邮箱已被注册"); } else { EditContext.MarkFieldAsValid(_fieldIdentifier); } } else { EditContext.MarkFieldAsValid(_fieldIdentifier); } } public void Dispose() { _fieldSubscription?.Dispose(); } }
然后在注册表单中使用这个组件:
<EditForm Model="@RegisterModel"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="mb-3"> <label class="form-label">邮箱</label> <InputText @bind-Value="RegisterModel.Email" class="form-control" /> <ValidationMessage For="@(() => RegisterModel.Email)" /> <EmailUniqueValidator For="@(() => RegisterModel.Email)" /> </div> <!-- 其他字段 --> </EditForm>
内容的提问来源于stack exchange,提问作者bacis09
相关产品推荐
相关产品推荐

