目錄 1.創建項目 2.重寫用戶管理界面 3.用戶管理 4.用戶註冊 5.用戶登錄 6.用戶退出 7.自動數據遷移 8.啟動應用 1.創建項目 項目中會多出如下紅框內的用戶身份管理的文件: 單獨啟動該項目,已經擁有了用戶登錄、註冊、註銷、管理等功能頁面。 2.重寫用戶管理界面 但是,我們並不想使用原 ...
目錄
1.創建項目
2.重寫用戶管理界面
3.用戶管理
4.用戶註冊
5.用戶登錄
6.用戶退出
7.自動數據遷移
8.啟動應用
1.創建項目
項目中會多出如下紅框內的用戶身份管理的文件:
單獨啟動該項目,已經擁有了用戶登錄、註冊、註銷、管理等功能頁面。
2.重寫用戶管理界面
但是,我們並不想使用原有的登錄頁面,我們想要自定義用戶頁面。
項目=》 右鍵 =》 添加 =》 新搭建基架的項目
添加過程中如果報錯 “運行所選代碼生成器時出錯”, 則先清理該項目,嘗試重新添加,如果仍然報錯,重啟VSStudio,再次添加基架即可。
然後就會看到項目中添加了下圖中的文件:
User.cs 中添加姓名和生日:
public class User : IdentityUser { [PersonalData] public string Name { get; set; } [PersonalData] public DateTime Birthday { get; set; } }
3.用戶管理
Leo.Users.Areas.Identity.Pages.Account.Manage.Index.cshtml.cs :
public partial class IndexModel : PageModel { private readonly UserManager<User> _userManager; private readonly SignInManager<User> _signInManager; private readonly IEmailSender _emailSender; public IndexModel( UserManager<User> userManager, SignInManager<User> signInManager, IEmailSender emailSender) { _userManager = userManager; _signInManager = signInManager; _emailSender = emailSender; } public string Username { get; set; } public bool IsEmailConfirmed { get; set; } [TempData] public string StatusMessage { get; set; } [BindProperty] public InputModel Input { get; set; } public class InputModel { #region 新增 [Required] [DataType(DataType.Text)] [Display(Name = "姓名")] public string Name { get; set; } [Required] [Display(Name = "生日")] [DataType(DataType.Date)] public DateTime Birthday { get; set; } #endregion [Required] [EmailAddress] public string Email { get; set; } [Phone] [Display(Name = "電話")] public string PhoneNumber { get; set; } } public async Task<IActionResult> OnGetAsync() { var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } var userName = await _userManager.GetUserNameAsync(user); var email = await _userManager.GetEmailAsync(user); var phoneNumber = await _userManager.GetPhoneNumberAsync(user); Username = userName; Input = new InputModel { #region 新增 Name = user.Name, Birthday = user.Birthday, #endregion Email = email, PhoneNumber = phoneNumber }; IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); return Page(); } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } var email = await _userManager.GetEmailAsync(user); if (Input.Email != email) { var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); if (!setEmailResult.Succeeded) { var userId = await _userManager.GetUserIdAsync(user); throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'."); } } #region 新增 if (Input.Name != user.Name) { user.Name = Input.Name; } if (Input.Birthday != user.Birthday) { user.Birthday = Input.Birthday; } #endregion var phoneNumber = await _userManager.GetPhoneNumberAsync(user); if (Input.PhoneNumber != phoneNumber) { var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); if (!setPhoneResult.Succeeded) { var userId = await _userManager.GetUserIdAsync(user); throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'."); } } #region 新增 await _userManager.UpdateAsync(user); #endregion await _signInManager.RefreshSignInAsync(user); StatusMessage = "Your profile has been updated"; return RedirectToPage(); } public async Task<IActionResult> OnPostSendVerificationEmailAsync() { if (!ModelState.IsValid) { return Page(); } var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } var userId = await _userManager.GetUserIdAsync(user); var email = await _userManager.GetEmailAsync(user); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.Page( "/Account/ConfirmEmail", pageHandler: null, values: new { userId = userId, code = code }, protocol: Request.Scheme); await _emailSender.SendEmailAsync( email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); StatusMessage = "Verification email sent. Please check your email."; return RedirectToPage(); } }
展示頁面 Index.cshtml :
@page @model IndexModel @{ ViewData["Title"] = "Profile"; ViewData["ActivePage"] = ManageNavPages.Index; } <h4>@ViewData["Title"]</h4> <partial name="_StatusMessage" for="StatusMessage" /> <div class="row"> <div class="col-md-6"> <form id="profile-form" method="post"> <div asp-validation-summary="All" 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.Email"></label> @if (Model.IsEmailConfirmed) { <div class="input-group"> <input asp-for="Input.Email" class="form-control" /> <span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span> </div> } else { <input asp-for="Input.Email" class="form-control" /> <button id="email-verification" type="submit" asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button> } <span asp-validation-for="Input.Email" class="text-danger"></span> </div> <div class="form-group"> @* 新增 start *@ <div class="form-group"> <label asp-for="Input.Name"></label> <input asp-for="Input.Name" class="form-control" /> </div> <div class="form-group"> <label asp-for="Input.Birthday"></label> <input asp-for="Input.Birthday" class="form-control" /> </div> @* end *@ <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> <button id="update-profile-button" type="submit" class="btn btn-primary">Save</button> </form> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
4.用戶註冊
Leo.Users.Areas.Identity.Pages.Account.Register.cshtml.cs :
[AllowAnonymous] public class RegisterModel : PageModel { private readonly SignInManager<User> _signInManager; private readonly UserManager<User> _userManager; private readonly ILogger<RegisterModel> _logger; private readonly IEmailSender _emailSender; public RegisterModel( UserManager<User> userManager, SignInManager<User> signInManager, ILogger<RegisterModel> logger, IEmailSender emailSender) { _userManager = userManager; _signInManager = signInManager; _logger = logger; _emailSender = emailSender; } [BindProperty] public InputModel Input { get; set; } public string ReturnUrl { get; set; } public class InputModel { #region 新增 [Required] [DataType(DataType.Text)] [Display(Name = "姓名")] public string Name { get; set; } [Required] [Display(Name = "生日")] [DataType(DataType.Date)] public DateTime Birthday { get; set; } #endregion [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "密碼")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "確認密碼")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public void OnGet(string returnUrl = null) { ReturnUrl = returnUrl; } public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) { var user = new User { #region 新增 Name = Input.Name, Birthday = Input.Birthday, #endregion UserName = Input.Email, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.Page( "/Account/ConfirmEmail", pageHandler: null, values: new { userId = user.Id, code = code }, protocol: Request.Scheme); await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); await _signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } // If we got this far, something failed, redisplay form return Page(); } }
展示頁面 Register.cshtml :
@page @model RegisterModel @{ ViewData["Title"] = "Register"; } <h1>@ViewData["Title"]</h1> <div class="row"> <div class="col-md-4"> <form asp-route-returnUrl="@Model.ReturnUrl" method="post"> <h4>Create a new account.</h4> <hr /> <div asp-validation-summary="All" class="text-danger"></div> @* 新增 start *@ <div class="form-group"> <label asp-for="Input.Name"></label> <input asp-for="Input.Name" class="form-control" /> <span asp-validation-for="Input.Name" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Birthday"></label> <input asp-for="Input.Birthday" class="form-control" /> <span asp-validation-for="Input.Birthday" class="text-danger"></span> </div> @* end *@ <div class="form-group"> <label asp-for="Input.Email"></label> <input asp-for="Input.Email" class="form-control" /> <span asp-validation-for="Input.Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Password"></label> <input asp-for="Input.Password" class="form-control" /> <span asp-validation-for="Input.Password" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.ConfirmPassword"></label> <input asp-for="Input.ConfirmPassword" class="form-control" /> <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary">Register</button> </form> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
5.用戶登錄
Login.cshtml.cs :
[AllowAnonymous] public class LoginModel : PageModel { private readonly SignInManager<User> _signInManager; private readonly ILogger<LoginModel> _logger; public LoginModel(SignInManager<User> signInManager, ILogger<LoginModel> logger) { _signInManager = signInManager; _logger = logger; } [BindProperty] public InputModel Input { get; set; } public IList<AuthenticationScheme> ExternalLogins { get; set; } public string ReturnUrl { get; set; } [TempData] public string ErrorMessage { get; set; } public class InputModel { [Required] [EmailAddress] public string Email { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } } public async Task OnGetAsync(string returnUrl = null) { if (!string.IsNullOrEmpty(ErrorMessage)) { ModelState.AddModelError(string.Empty, ErrorMessage); } returnUrl = returnUrl ?? Url.Content("~/"); // Clear the existing external cookie to ensure a clean login process await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); ReturnUrl = returnUrl; } public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) { // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); if (result.Succeeded) { _logger.LogInformation("User logged in."); return LocalRedirect(returnUrl); } if (result.RequiresTwoFactor) { return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); } if (result.IsLockedOut) { _logger.LogWarning("User account locked out."); return RedirectToPage("./Lockout"); } else { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return Page(); } } // If we got this far, something failed, redisplay form return Page(); } }
Login.cshtml :
@page @model LoginModel @{ ViewData["Title"] = "Log in"; } <h1>@ViewData["Title"]</h1> <div class="row"> <div class="col-md-4"> <section> <form id="account" method="post"> <h4>Use a local account to log in.</h4> <hr /> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <label asp-for="Input.Email"></label> <input asp-for="Input.Email" class="form-control" /> <span asp-validation-for="Input.Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Password"></label> <input asp-for="Input.Password" class="form-control" /> <span asp-validation-for="Input.Password" class="text-danger"></span> </div> <div class="form-group"> <div class="checkbox"> <label asp-for="Input.RememberMe"> <input asp-for="Input.RememberMe" /> @Html.DisplayNameFor(m => m.Input.RememberMe) </label> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Log in</button> </div> <div class="form-group"> <p> <a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a> </p> <p> <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a> </p> </div> </form> </section> </div> <div class="col-md-6 col-md-offset-2"> <section> <h4>Use another service to log in.</h4> <hr /> @{ if ((Model.ExternalLogins?.Count ?? 0) == 0) { <div> <p> There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a> for details on setting up this ASP.NET application to support logging in via external services. </p> </div> } else { <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal"> <div> <p> @foreach (var provider in Model.ExternalLogins) { <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button> } </p> </div> </form> } } </section> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
6.用戶退出
Logout.cshtml.cs :
[AllowAnonymous] public class LogoutModel : PageModel { private readonly SignInManager<User> _signInManager; private readonly ILogger<LogoutModel> _logger; public LogoutModel(SignInManager<User> signInManager, ILogger<LogoutModel> logger) { _signInManager = signInManager; _logger = logger; } public void OnGet() { } public async Task<IActionResult> OnPost(string returnUrl = null) { await _signInManager.SignOutAsync(); _logger.LogInformation("User logged out."); if (returnUrl != null) { return LocalRedirect(returnUrl); } else { return Page(); } } }
Logout.cshtml :
@page @model LogoutModel @{ ViewData["Title"] = "Log out"; } <header> <h1>@ViewData["Title"]</h1> <p>您已成功退出</p> </header>
7.自動數據遷移
程式自動完成資料庫以及表的構建
如圖,會報錯,是因為項目中有兩個Context 數據上下文:
刪掉下麵的包含 ApplicationDbContext.cs 的 Data文件夾
然後編譯時startup.cs 會報錯,找不到ApplicationDbContext 類,此時我們直接將這一段註釋掉即可,因為我們添加基架以後已經有了新的數據模塊:
public class Startup {