- Các chức năng quản lý tài khoản cá nhân
- Cập nhật trang Profile (Index) thêm trường dữ liệu vào User
- Trang quản lý Email của User
- Trang đổi password
- Quản lý đăng ký từ dịch vụ ngoài Google, Facebook
Bài này tiếp tục thực hành, phát triển trên dự án của ví dụ Album trước: Identity (4) - Ứng dụng Album
Các chức năng quản lý tài khoản cá nhân trong Identity
Mã nguồn mẫu phát sinh của Identity mà đã sử dụng trong các buổi trước nó còn có các trang
Razor Page nằm ở thư mục Areas/Identity/Pages/Account/Manage
nó gồm các trang Razor để
User quản lý tài khoản của chính mihf như:
Index
- hiện thị thông tin cơ bản của tài khoảnEmail
- thay đổi email tài khoảnPassword
- đổi passwordExternalLogins
- liên kết với các dịch vụ ngoài Facebook, Google ... nếu trang hỗ trợPersonalData
- tải thông tinn cá nhân, xóa tài khoảnKhi tài khoản đăng nhập, truy cập đến các chức năng này bắt đầu ở địa chỉ https://localhost:5001/identity/account/manage
Để các chức năng hoạt động bạn có thể tùy biến như sau:
Thêm vào file _ViewStart.cshtml
ở thư mục Areas/Identity/Pages/Account/Manage
với nội dung:
@{ Layout = "_Layout"; }
Nó sẽ thiết lập tất cả cac trang này sử dụng _Layout tại thư mục hiện tại của chúng, tức dùng
Areas/Identity/Pages/Account/Manage/_Layout.cshtml
File Areas/Identity/Pages/Account/Manage/_Layout.cshtml
cập nhật thành
@{ if (ViewData.TryGetValue("ParentLayout", out var parentLayout)) { Layout = (string)parentLayout; } else { // Layout = "/Areas/Identity/Pages/_Layout.cshtml"; // Sử dụng tiếp Layout chung /Pages/Shared/_Layout.cshtml Layout = "/Pages/Shared/_Layout.cshtml"; } } <h2>QUẢN LÝ TÀI KHOẢN</h2> <div> <h4>Cập nhật thông tin tài khoản của bạn</h4> <hr /> <div class="row"> <div class="col-md-3"> <partial name="_ManageNav" /> </div> <div class="col-md-9"> @RenderBody() </div> </div> </div> @section Scripts { @RenderSection("Scripts", required: false) }
Giao diện truy cập sẽ là:
Thêm trường dữ liệu User, cập nhật trang Profile
Trong bàng User phát sinh theo Model - AppUser, giờ mong muốn ngoài các trường mặc định
kế thừa từ IdentityUser
bạn muốn thêm một vài trường dữ liệu nữa ví dụ bạn muốn thêm
các trường: FullName, Address, Birthday
using System; using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Identity; namespace Album.Models { public class AppUser : IdentityUser { [MaxLength(100)] public string FullName {set; get;} [MaxLength(255)] public string Address {set; get;} [DataType(DataType.Date)] public DateTime? Birthday {set; get;} } }
Nếu muốn thiết lập các trường phức tạp hơn bạn có thể dùng kỹ thuật Fluent API trong EF (EF Core) Tạo quan hệ trong Entity Framework với Fluent API C# CSharp
Thực hiện tạo Migration đặt tên updateuser và cập nhật vào Database
dotnet ef migrations add updateuser dotnet ef database update
Sau khi cập nhật CSDL bảng User có thêm các trường mới thêm vào
Sửa đổi trang Hồ sơ cho phép cập nhật các trường bổ sung trên
Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
namespace Album.Areas.Identity.Pages.Account.Manage { public partial class IndexModel : PageModel { private readonly UserManager<AppUser> _userManager; private readonly SignInManager<AppUser> _signInManager; public IndexModel ( UserManager<AppUser> userManager, SignInManager<AppUser> signInManager) { _userManager = userManager; _signInManager = signInManager; } [Display(Name = "Tên tài khoản")] public string Username { get; set; } [TempData] public string StatusMessage { get; set; } [BindProperty] public InputModel Input { get; set; } public class InputModel { [Phone] [Display (Name = "Số điện thoại")] public string PhoneNumber { get; set; } [MaxLength (100)] [Display(Name = "Họ tên đầy đủ")] public string FullName { set; get; } [MaxLength (255)] [Display(Name = "Địa chỉ")] public string Address { set; get; } [DataType (DataType.Date)] [Display(Name = "Ngày sinh d/m/y")] public DateTime? Birthday { set; get; } } // Nạp thông tin từ User vào Model private async Task LoadAsync (AppUser user) { var userName = await _userManager.GetUserNameAsync (user); var phoneNumber = await _userManager.GetPhoneNumberAsync (user); Username = userName; Input = new InputModel { PhoneNumber = phoneNumber, Birthday = user.Birthday, Address = user.Address, FullName = user.FullName }; } public async Task<IActionResult> OnGetAsync () { var user = await _userManager.GetUserAsync (User); if (user == null) { return NotFound ($"Không tải được tài khoản ID = '{_userManager.GetUserId(User)}'."); } await LoadAsync (user); return Page(); } public async Task<IActionResult> OnPostAsync () { var user = await _userManager.GetUserAsync (User); if (user == null) { return NotFound ($"Không có tài khoản ID: '{_userManager.GetUserId(User)}'."); } if (!ModelState.IsValid) { await LoadAsync(user); return Page (); } var phoneNumber = await _userManager.GetPhoneNumberAsync (user); if (Input.PhoneNumber != phoneNumber) { var setPhoneResult = await _userManager.SetPhoneNumberAsync (user, Input.PhoneNumber); if (!setPhoneResult.Succeeded) { StatusMessage = "Lỗi cập nhật số điện thoại."; return RedirectToPage (); } } // Cập nhật các trường bổ sung user.Address = Input.Address; user.Birthday = Input.Birthday; user.FullName = Input.FullName; await _userManager.UpdateAsync(user); // Đăng nhập lại để làm mới Cookie (không nhớ thông tin cũ) await _signInManager.RefreshSignInAsync (user); StatusMessage = "Hồ sơ của bạn đã cập nhật"; return RedirectToPage (); } } }
Areas/Identity/Pages/Account/Manage/Index.cshtml
@page @model IndexModel @{ ViewData["Title"] = "HỒ SƠ"; ViewData["ActivePage"] = ManageNavPages.Index; } <h4>@ViewData["Title"]</h4> <partial name="_StatusMessage" model="Model.StatusMessage" /> <div class="row"> <div class="col-md-6"> <form id="profile-form" method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Username"></label> <input asp-for="Username" class="form-control" disabled /> </div> <div class="form-group"> <label asp-for="Input.PhoneNumber"></label> <input asp-for="Input.PhoneNumber" class="form-control" /> <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.FullName"></label> <input asp-for="Input.FullName" class="form-control" /> <span asp-validation-for="Input.FullName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Address"></label> <input asp-for="Input.Address" class="form-control" /> <span asp-validation-for="Input.Address" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Birthday"></label> @Html.TextBoxFor(m => m.Input.Birthday, "{0:d}", new {@class = "form-control"}) <span asp-validation-for="Input.Birthday" class="text-danger"></span> </div> <button id="update-profile-button" type="submit" class="btn btn-danger">Lưu hồ sơ</button> </form> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
Khi truy cập cập nhật hồ sơ, các trường bổ sung được lưu lại
Đối với dữ liệu ngày sinh bạn có thể thiết lập hiện thị ở định dạng chuỗi dd/MM/yyyy
ngày - tháng - năm. Để thực hiện hãy sửa Areas/Identity/Pages/Account/Manage/Index.cshtml
dùng Html.TextBoxFor
để tạo HTML của Birthday
<div class="form-group"> <label asp-for="Input.Birthday"></label> @Html.TextBoxFor(m => m.Input.Birthday, "{0:dd/MM/yyyy}", new {@class = "form-control"}) <span asp-validation-for="Input.Birthday" class="text-danger"></span> </div>
Tuy nhiên khi thiết lập hiện thị ở định dạng như vậy, khi thực hiện binding dữ liệu có thể dẫn tới lỗi. Trong trường hợp này bạn có thể áp dụng Xây dựng ModelBinder riêng
Ví dụ tạo ra lớp DayMonthYearBinder
Binder/DayMonthYearBinder.cs
public class DayMonthYearBinder : IModelBinder { public Task BindModelAsync (ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException (nameof (bindingContext)); } // Lấy tên Model (Object, thuộc tính ...) string modelName = bindingContext.ModelName; // Lấy giá trị truyền đến tương ứng với modelName ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue (modelName); // Không có giá trị if (valueProviderResult == ValueProviderResult.None) { return Task.CompletedTask; } // Thiết lập và lấy giá trị (đầu tiên) bindingContext.ModelState.SetModelValue (modelName, valueProviderResult); string valueStrinng = valueProviderResult.FirstValue; // Xử lý nếu giá trị truyền đến là null if (string.IsNullOrEmpty (valueStrinng)) { return Task.CompletedTask; } // Chuyển chuỗi giá trị thành DateTime DateTime? date = null; try { date = DateTime.ParseExact (valueStrinng, "dd/MM/yyyy", null); } catch { bindingContext.ModelState.TryAddModelError (modelName, "Nhập ngày tháng bị sai - yêu cầu định dạng dd/MM/yyyy (ví dụ 20/11/2020)"); return Task.CompletedTask; } if (date < DateTime.Parse ("1/1/1945")) { bindingContext.ModelState.TryAddModelError (modelName, "Abcs"); return Task.CompletedTask; } // Gán giá trị thành công bindingContext.Result = ModelBindingResult.Success (date); return Task.CompletedTask; } }
Sau đó áp dụng vào thiết lập Binding của trường Birthday
[DataType (DataType.Date)] [Display(Name = "Ngày sinh d/m/y")] [ModelBinder(BinderType=typeof(DayMonthYearBinder))] [DisplayFormat(ApplyFormatInEditMode=true, DataFormatString = "{0:dd/MM/yyyy}")] public DateTime? Birthday { set; get; }
Lưu ý
-
Để tải về User theo thông tin đang đăng nhập dùng UserManager gọi GetUserAsync
var user = await _userManager.GetUserAsync (User);
-
Các của User gồm
UserName
,Email
,PhoneNumber
có phương thức trong UserManager để truy vấn và thiết lập thông tin đó tương ứng theo tên như: GetEmailAsync(), SetEmailAsync ... -
Để cập nhật thông tin User có thể dùng phương thức
UpdateAsync
của -
Để đăng nhập lại theo thông tin mới cập nhật sử dụng
await _signInManager.RefreshSignInAsync (user);
Trang quản lý Email của User
Mã code trang này tại
Areas/Identity/Pages/Account/Manage/Email.cshtml.cs
, có chức năng để
người dùng thay đổi Email của mình thành một địa chỉ email khác.
Khi thay đổi - một Email được gửi đến Email mới để xác nhận, trong email này có mã Token phát sinh bởi:
var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
Nếu người dùng mở Email và truy cập theo link hướng dẫn thì nó sẽ chuyển đến trang
thực hiện thay đổi email thực sự,code trang này tại
Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs
Thực hiện thay đổi Email bằng
var result = await _userManager.ChangeEmailAsync(user, email, code);
Nếu code chính xác, email mới chưa ai dùng thì đổi email thành công
Trang đổi password của User
Trang này tại Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs
có chức
năng cho phép User đổi password mới.
Để đổi password thực hiện đoạn mã:
var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
Chú ý:
Trước khi thiết lập mật khẩu cần kiểm tra tài khoản có mật khẩu hay không bằng
var hasPassword = await _userManager.HasPasswordAsync(user);
Nếu tài khoản chưa từng đặt mật khẩu (ví dụ khi đăng ký từ dịch vụ ngoài Google, Facebook ...) thì
chuyển hướng về trang Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs
tại trang này sẽ cho đặt mặt khẩu mới bằng cách sử dụng
var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword);
Trang quản lý đăng nhập từ Google, Facebook ...
Trang này code tại Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs
,
có chức năng để User tích hợp đăng ký từ dịch vụ ngoài theo tính năng ứng dụng hỗ trợ như Google,
Facebook ... hoặc để loại bỏ liên kết tài khoản từ dịch vụ ngoài
Để lấy các dịch vụ đã liên kết sử dụng
CurrentLogins = await _userManager.GetLoginsAsync(user);
Để lấy các dịch vụ ứng dụng hỗ trợ mà tài khoản chưa liên kết sử dụng
OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) .ToList();
Để xóa bỏ một liên kết thực hiện
var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey);
Để liên kết với dịch vụ chưa đăng ký cần thực hiện đăng xuất thông tin đang liên kết với tài khoản ngoài nếu có
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
Sau đó chuyển hướng xác thực từ tài khoản này, sau khi xác thực sẽ chuyển hướngg về OnGetLinkLoginCallbackAsync trong ExternalLogins.cshtml.cs để thực hiện liên kết, tương từ như trang Login
Mã nguồn tham khảo ASP_NET_CORE/Album