iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
SideProject30

我想自己刻部落格系列 第 26

建立帳號相關 UserService

  • 分享至 

  • xImage
  •  

目前應該處理了文章的相關功能,但是缺少登入登出等帳號相關部分,發的文章其實都是無名氏 XDD

今天來補這一塊。

首先先定義 IService 介面,先定義這幾個,不然今天寫不完。

public interface IUserService
{
    public Task<User?> GetAsync(int id);

    public Task InitCreateAsync(UserViewModel viewModel);

    public Task CreateAsync(UserViewModel viewModel);

    public Task UpdateAsync(UserViewModel viewModel);
}

介面中四個方法比較特別的是 InitCreateAsync 因為我希望第一位註冊的人是最大權限管理者。除了權限設定外,其餘邏輯跟 CreateAsync 一樣。

定義 UserViewModel

先定義 UserViewModel 讓我們在註冊與編輯時使用,不直接使用原本的 User 物件原因是時間欄位不是註冊跟編輯需樣的,另外加上 User 物件要加驗證屬性的話,需要使用 partial class 與 MetadataType,不然使用 EF Core 反向工程時會被覆蓋到,不如直接做 ViewModel。

這裡模型驗證需檢查的項目必填 [Required] 、信箱格式[EmailAddress]、密碼相同[Compare],另外我實做了 IValidatableObject 介面的 Validate 方法,來幫我檢查此信箱跟DisplayId有沒有被重複使用,最後實作了 ToUser() 方法,幫我轉成 User 物件。

public class UserViewModel : IValidatableObject
{
    public int Id { get; set; }

    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;

    [Required]
    public string Password { get; set; } = string.Empty;

    [Compare(nameof(Password))]
    public string PasswordCheck { get; set; } = string.Empty;

    [Required]
    public string Name { get; set; } = string.Empty;

    [Required]
    public string DisplayId { get; set; } = string.Empty;

    public int Role { get; set; }
    
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.IsNullOrWhiteSpace(Email))
        {
            yield break;
        }

        var userRepository = validationContext.GetService<IUserRepository>();
        var emailUser = userRepository.Query(x => x.Email == Email).FirstOrDefault();
        if (emailUser is not null && emailUser.Id != Id)
        {
            yield return new ValidationResult("此 Email 已存在", new[] { nameof(Email) });
        }

        var displayIdUser = userRepository.Query(x => x.DisplayId == DisplayId).FirstOrDefault();
        if (displayIdUser is not null && displayIdUser.Id != Id)
        {
            yield return new ValidationResult("此 DisplayId 已存在", new[] { nameof(DisplayId) });
        }
    }

    public User ToUser()
    {
        var user = new User();
        user.Id = Id;
        user.Name = Name;
        user.Email = Email;
        user.Password = Password;
        user.DisplayId = DisplayId;
        user.Role = Role;
        user.CreateDate = new DateTime();
        user.UpdateDate = user.CreateDate;

        return user;
    }
}

現在開始實作 UserService ,其中建構是注入除了,IUserRepository 之外,我還注入了 PasswordHasher ,幫密碼做 Hash 再存入。

public UserService(IUserRepository userRepository, PasswordHasher<User> passwordHasher)
{
    _userRepository = userRepository;
    _passwordHasher = passwordHasher;
}

public async Task<User?> GetAsync(int id)
{
    var user = await _userRepository.GetAsync(x => x.Id == id);

    return user;
}

HashUserPasswordVerifyHashedPassword ,分別做 Hash 雜湊,與驗證使用者密碼,明天做登入時會需要。

private void HashUserPassword(User user)
{
    var hashedPassword = _passwordHasher.HashPassword(user, user.Password);
    user.Password = hashedPassword;
}

private PasswordVerificationResult VerifyHashedPassword(User user, string password)
{
    return _passwordHasher.VerifyHashedPassword(user, user.Password, password);
}

最後是 CreateAsync 和 UpdateAsync

public async Task CreateAsync(UserViewModel viewModel)
{
    var user = viewModel.ToUser();
    HashUserPassword(user);
    user.Role = viewModel.Role;
    user.CreateDate = DateTime.Now;
    user.UpdateDate = user.CreateDate;
    await _userRepository.CreateAsync(user);
    _userRepository.UnitOfWork.Save();
}

public async Task UpdateAsync(UserViewModel viewModel)
{
    var user = await _userRepository.GetAsync(x => x.Id == viewModel.Id);
    if (user is null)
    {
        return;
    }

    user.Name = viewModel.Name;
    user.Email = viewModel.Email;
    user.Password = viewModel.Password;
    user.DisplayId = viewModel.DisplayId;
    HashUserPassword(user);
    user.UpdateDate = DateTime.Now;
    _userRepository.UnitOfWork.Save();
}

實際程式碼以 GitHub 上為主。


上一篇
建立 sitemap.xml
下一篇
建立註冊、登入、登出
系列文
我想自己刻部落格31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言