Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule pokazano, jak:
- Dodawanie niestandardowych danych użytkownika do aplikacji internetowej ASP.NET Core.
- Oznacz niestandardowy model danych użytkownika za pomocą atrybutu PersonalDataAttribute , aby był on automatycznie dostępny do pobrania i usunięcia. Udostępnianie danych do pobrania i usunięcia pomaga spełnić wymagania RODO .
Przykładowy projekt jest tworzony na podstawie aplikacji internetowej Razor Pages, ale instrukcje są podobne dla aplikacji internetowej MVC ASP.NET Core.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Prerequisites
Zestaw SDK platformy .NET 10.0
Szybki start: dodawanie dwóch właściwości niestandardowych
W przypadku typowego przypadku dodawania dwóch właściwości niestandardowych, takich jak FirstName i LastName:
- Tworzenie nowego projektu ASP.NET Core z uwierzytelnianiem poszczególnych kont
- Wygeneruj szkielet Identity, aby zastąpić strony Register i Manage/Index
- Dodaj właściwości niestandardowe, takie jak
FirstNameiLastNamedo klasy użytkownika, z atrybutem[PersonalData]. - Zaktualizuj strony Register i Manage/Index, aby zawierały pola do wprowadzania danych dla Twoich właściwości.
- Tworzenie i stosowanie migracji.
- Rejestracja testów i zarządzanie profilami.
Poniższe sekcje zawierają szczegółowe instrukcje krok po kroku dotyczące tego procesu.
Tworzenie aplikacji internetowej
Możesz utworzyć aplikację internetową Razor Pages lub MVC. Oba używają tego samego IdentityRazor interfejsu użytkownika stron.
Razor Stron
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Plik w programie Visual Studio wybierz pozycję Nowy>Projekt/Rozwiązanie....
- Wybierz ASP.NET Core Web App (Razor Strony)>Dalej
- Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania . Wybierz Dalej.
- Wybierz Typ uwierzytelniania>Konta indywidualne>Utwórz
- Skompiluj i uruchom projekt.
MVC
- Visual Studio
- interfejs wiersza polecenia .NET
- W menu Plik programu Visual Studio wybierz pozycję Nowy>Projekt/Rozwiązanie....
- Wybierz aplikację internetową ASP.NET Core (model-View-Controller)>Dalej
- Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania . Wybierz Dalej.
- Wybierz Typ uwierzytelniania>Konta indywidualne>Utwórz
- Skompiluj i uruchom projekt.
Note
Identity Interfejs użytkownika jest implementowany jako Razor biblioteka klas. Podczas tworzenia projektu MVC z uwierzytelnianiem strony Identity są udostępniane z biblioteki klas Razor, co oznacza, że projekt MVC używa stron Razor do obsługi Identity, mimo że w pozostałej części projektu są używane kontrolery i widoki MVC.
Uruchamianie szkieletu Identity
- Visual Studio
- interfejs wiersza polecenia .NET
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt >Add>Nowy element szkieletowy.
- W lewym panelu okna dialogowego Dodawanie szkieletu wybierz Identity>Dodaj.
- W oknie dialogowym Dodaj Identity są następujące opcje:
- Wybierz istniejący plik układu
~/Pages/Shared/_Layout.cshtmldla stron Razor lub~/Views/Shared/_Layout.cshtmldla MVC - Wybierz następujące pliki do zastąpienia:
- Account/Register
- Account/Manage/Index
- Wybierz przycisk , + aby utworzyć nową klasę kontekstu danych. Zaakceptuj typ (WebApp1.Models.WebApp1Context , jeśli projekt ma nazwę WebApp1).
- Wybierz przycisk , + aby utworzyć nową klasę Użytkownik. Zaakceptuj typ (WebApp1User , jeśli projekt ma nazwę WebApp1) >Dodaj.
- Wybierz istniejący plik układu
- Wybierz Dodaj.
Postępuj zgodnie z instrukcjami w sekcji Migrations, UseAuthentication, and layout, aby wykonać następujące czynności:
- Utwórz migrację i zaktualizuj bazę danych.
- Dodaj
UseAuthenticationdoProgram.cs - Dodaj
<partial name="_LoginPartial" />do pliku układu. - Przetestuj aplikację:
- Rejestrowanie użytkownika
- Wybierz nową nazwę użytkownika (obok linku Wyloguj ). Może być konieczne rozwinięcie okna lub wybranie ikony paska nawigacyjnego, aby wyświetlić nazwę użytkownika i inne linki.
- Wybierz kartę Dane osobowe .
- Wybierz przycisk Pobierz i sprawdź
PersonalData.jsonplik. - Przetestuj przycisk Usuń , który usuwa zalogowanego użytkownika.
Dodawanie niestandardowych danych użytkownika do Identity bazy danych
Zaktualizuj klasę pochodną IdentityUser za pomocą właściwości niestandardowych. Jeśli nazwano projekt WebApp1, plik ma nazwę Areas/Identity/Data/WebApp1User.cs. Zaktualizuj plik przy użyciu następującego kodu:
using Microsoft.AspNetCore.Identity;
namespace WebApp1.Areas.Identity.Data;
public class WebApp1User : IdentityUser
{
[PersonalData]
public string? Name { get; set; }
[PersonalData]
public DateTime DOB { get; set; }
}
Właściwości z atrybutem PersonalData to:
- Usunięto, gdy strona wywołuje metodę
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete. - Dane pobrane i uwzględnione przez stronę
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor.
Account/Manage/Index.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Manage/Index.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class IndexModel : PageModel
{
private readonly UserManager<WebApp1User> _userManager;
private readonly SignInManager<WebApp1User> _signInManager;
public IndexModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public string Username { get; set; }
// Remaining API warnings omited.
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
private async Task LoadAsync(WebApp1User user)
{
var userName = await _userManager.GetUserNameAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Name = user.Name,
DOB = user.DOB,
PhoneNumber = phoneNumber
};
}
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)}'.");
}
await LoadAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with 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 = "Unexpected error when trying to set phone number.";
return RedirectToPage();
}
}
if (Input.Name != user.Name)
{
user.Name = Input.Name;
}
if (Input.DOB != user.DOB)
{
user.DOB = Input.DOB;
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Manage/Index.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
ViewData["ActivePage"] = ManageNavPages.Index;
}
<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="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-floating">
<input asp-for="Username" class="form-control" disabled />
<label asp-for="Username" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.PhoneNumber" class="form-control" />
<label asp-for="Input.PhoneNumber" class="form-label"></label>
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Account/Register.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Register.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class RegisterModel : PageModel
{
private readonly SignInManager<WebApp1User> _signInManager;
private readonly UserManager<WebApp1User> _userManager;
private readonly IUserStore<WebApp1User> _userStore;
private readonly IUserEmailStore<WebApp1User> _emailStore;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<WebApp1User> userManager,
IUserStore<WebApp1User> userStore,
SignInManager<WebApp1User> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_userStore = userStore;
_emailStore = GetEmailStore();
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[BindProperty]
public InputModel Input { get; set; }
// Remaining API warnings omited.
public string ReturnUrl { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
user.Name = Input.Name;
user.DOB = Input.DOB;
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
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>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
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();
}
private WebApp1User CreateUser()
{
try
{
return Activator.CreateInstance<WebApp1User>();
}
catch
{
throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
$"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
$"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
}
}
private IUserEmailStore<WebApp1User> GetEmailStore()
{
if (!_userManager.SupportsUserEmail)
{
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore<WebApp1User>)_userStore;
}
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Register.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB"></label>
<span asp-validation-for="Input.DOB" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-6 col-md-offset-2">
<section>
<h3>Use another service to register.</h3>
<hr />
@{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</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" />
}
Skompiluj projekt.
Aktualizowanie układu
Zobacz Zmiany układu , aby uzyskać instrukcje dotyczące dodawania linków logowania i wylogowyń do każdej strony.
Dodaj migrację dla niestandardowych danych użytkownika
- Visual Studio
- interfejs wiersza polecenia .NET
W konsoli Visual Studio Menedżer pakietów:
Add-Migration CustomUserData
Update-Database
Testowanie tworzenia, wyświetlania, pobierania, usuwania niestandardowych danych użytkownika
Przetestuj aplikację:
- Zarejestruj nowego użytkownika.
- Wyświetl niestandardowe dane użytkownika na
/Identity/Account/Managestronie. - Pobierz i wyświetl dane osobowe użytkowników ze
/Identity/Account/Manage/PersonalDatastrony.
Troubleshooting
Jeśli wystąpią problemy:
| Objaw | Prawdopodobna przyczyna | Rozwiązanie |
|---|---|---|
| Pola niestandardowe nie są wyświetlane w formularzu rejestracji | Zapomniano zaktualizować plik Register.cshtml | Upewnij się, że wygenerowano szkielet i zmodyfikowano Areas/Identity/Pages/Account/Register.cshtml |
| Dane niestandardowe nie są zapisywane | Zapomniałeś dodać migrację lub zaktualizować bazę danych | Uruchom dotnet ef migrations add CustomUserData, a następnie dotnet ef database update |
| Pola niestandardowe niewystępujące w pobranych danych | Brak atrybutu [PersonalData] |
Dodawanie [PersonalData] atrybutu do właściwości w klasie użytkownika |
| Błędy kompilacji po wygenerowaniu szkieletu | Niezgodność wersji pakietu | Upewnij się, że wszystkie Identity pakiety są zgodne z wersją platformy docelowej |
Note
Przykłady kodu i instrukcje dotyczące .NET 9 odwołują się do folderu przykładowego 10.x. W tym scenariuszu kod jest identyczny w .NET 9 i .NET 10. Tylko platforma docelowa i wersje pakietów różnią się w pliku projektu.
Szybki start: dodawanie dwóch właściwości niestandardowych
W przypadku typowego przypadku dodawania dwóch właściwości niestandardowych, takich jak FirstName i LastName:
- Tworzenie nowego projektu ASP.NET Core z uwierzytelnianiem poszczególnych kont
- Wygeneruj szkielet Identity, aby zastąpić strony Register i Manage/Index
- Dodaj właściwości niestandardowe, takie jak
FirstNameiLastNamedo klasy użytkownika, z atrybutem[PersonalData]. - Zaktualizuj strony Rejestracja oraz Zarządzanie/Indeks, aby uwzględnić pola dla swoich właściwości
- Utwórz i zastosuj migrację
- Rejestracja testowa i zarządzanie profilem
Poniższe sekcje zawierają szczegółowe instrukcje krok po kroku dotyczące tego procesu.
Tworzenie aplikacji internetowej
Możesz utworzyć aplikację internetową Razor Pages lub MVC. Oba używają tego samego IdentityRazor interfejsu użytkownika stron.
Razor Stron
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz Aplikacja internetowa ASP.NET Core>Dalej
- Wybierz Typ uwierzytelniania>Konta indywidualne>Utwórz
- Skompiluj i uruchom projekt.
MVC
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz aplikację internetową ASP.NET Core (model-View-Controller)>Dalej
- Wybierz Typ uwierzytelniania>Konta indywidualne>Utwórz
- Skompiluj i uruchom projekt.
Note
Identity Interfejs użytkownika jest implementowany jako Razor biblioteka klas. Podczas tworzenia projektu MVC z uwierzytelnianiem strony Identity są udostępniane z biblioteki klas Razor, co oznacza, że projekt MVC używa stron Razor do obsługi Identity, mimo że w pozostałej części projektu używane są kontrolery i widoki MVC.
Uruchamianie szkieletu Identity
- Visual Studio
- interfejs wiersza polecenia .NET
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt >Add>Nowy element szkieletowy.
- W lewym panelu okna dialogowego Dodawanie szkieletu wybierz Identity>Dodaj.
- W oknie dialogowym Dodaj Identity są następujące opcje:
- Wybierz istniejący plik układu
~/Pages/Shared/_Layout.cshtmldla stron Razor lub~/Views/Shared/_Layout.cshtmldla MVC - Wybierz następujące pliki do zastąpienia:
- Account/Register
- Account/Manage/Index
- Wybierz przycisk , + aby utworzyć nową klasę kontekstu danych. Zaakceptuj typ (WebApp1.Models.WebApp1Context , jeśli projekt ma nazwę WebApp1).
- Wybierz przycisk , + aby utworzyć nową klasę Użytkownik. Zaakceptuj typ (WebApp1User , jeśli projekt ma nazwę WebApp1) >Dodaj.
- Wybierz istniejący plik układu
- Wybierz Dodaj.
Postępuj zgodnie z instrukcjami w Migracje, UseAuthentication i układ strony, aby wykonać następujące czynności:
- Utwórz migrację i zaktualizuj bazę danych.
- Dodaj
UseAuthenticationdoProgram.cs - Dodaj
<partial name="_LoginPartial" />do pliku układu. - Przetestuj aplikację:
- Rejestrowanie użytkownika
- Wybierz nową nazwę użytkownika (obok linku Wyloguj ). Może być konieczne rozwinięcie okna lub wybranie ikony paska nawigacyjnego, aby wyświetlić nazwę użytkownika i inne linki.
- Wybierz kartę Dane osobowe .
- Wybierz przycisk Pobierz i sprawdź
PersonalData.jsonplik. - Przetestuj przycisk Usuń , który usuwa zalogowanego użytkownika.
Dodawanie niestandardowych danych użytkownika do Identity bazy danych
Zaktualizuj klasę pochodną IdentityUser za pomocą właściwości niestandardowych. Jeśli nazwano projekt WebApp1, plik ma nazwę Areas/Identity/Data/WebApp1User.cs. Zaktualizuj plik przy użyciu następującego kodu:
using Microsoft.AspNetCore.Identity;
namespace WebApp1.Areas.Identity.Data;
public class WebApp1User : IdentityUser
{
[PersonalData]
public string? Name { get; set; }
[PersonalData]
public DateTime DOB { get; set; }
}
Właściwości z atrybutem PersonalData to:
- Usunięto, gdy strona wywołuje metodę
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete. - Dane pobrane i uwzględnione przez stronę
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor.
Account/Manage/Index.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Manage/Index.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class IndexModel : PageModel
{
private readonly UserManager<WebApp1User> _userManager;
private readonly SignInManager<WebApp1User> _signInManager;
public IndexModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public string Username { get; set; }
// Remaining API warnings omited.
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
private async Task LoadAsync(WebApp1User user)
{
var userName = await _userManager.GetUserNameAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Name = user.Name,
DOB = user.DOB,
PhoneNumber = phoneNumber
};
}
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)}'.");
}
await LoadAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with 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 = "Unexpected error when trying to set phone number.";
return RedirectToPage();
}
}
if (Input.Name != user.Name)
{
user.Name = Input.Name;
}
if (Input.DOB != user.DOB)
{
user.DOB = Input.DOB;
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Manage/Index.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
ViewData["ActivePage"] = ManageNavPages.Index;
}
<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="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-floating">
<input asp-for="Username" class="form-control" disabled />
<label asp-for="Username" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.PhoneNumber" class="form-control" />
<label asp-for="Input.PhoneNumber" class="form-label"></label>
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Account/Register.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Register.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class RegisterModel : PageModel
{
private readonly SignInManager<WebApp1User> _signInManager;
private readonly UserManager<WebApp1User> _userManager;
private readonly IUserStore<WebApp1User> _userStore;
private readonly IUserEmailStore<WebApp1User> _emailStore;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<WebApp1User> userManager,
IUserStore<WebApp1User> userStore,
SignInManager<WebApp1User> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_userStore = userStore;
_emailStore = GetEmailStore();
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[BindProperty]
public InputModel Input { get; set; }
// Remaining API warnings omited.
public string ReturnUrl { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
user.Name = Input.Name;
user.DOB = Input.DOB;
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
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>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
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();
}
private WebApp1User CreateUser()
{
try
{
return Activator.CreateInstance<WebApp1User>();
}
catch
{
throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
$"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
$"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
}
}
private IUserEmailStore<WebApp1User> GetEmailStore()
{
if (!_userManager.SupportsUserEmail)
{
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore<WebApp1User>)_userStore;
}
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Register.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB"></label>
<span asp-validation-for="Input.DOB" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-6 col-md-offset-2">
<section>
<h3>Use another service to register.</h3>
<hr />
@{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</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" />
}
Skompiluj projekt.
Aktualizowanie układu
Zobacz Zmiany układu , aby uzyskać instrukcje dotyczące dodawania linków logowania i wylogowyń do każdej strony.
Dodaj migrację dla niestandardowych danych użytkownika
- Visual Studio
- interfejs wiersza polecenia .NET
W konsoli Visual Studio Menedżer pakietów:
Add-Migration CustomUserData
Update-Database
Testowanie tworzenia, wyświetlania, pobierania, usuwania niestandardowych danych użytkownika
Przetestuj aplikację:
- Zarejestruj nowego użytkownika.
- Wyświetl niestandardowe dane użytkownika na
/Identity/Account/Managestronie. - Pobierz i wyświetl dane osobowe użytkowników ze
/Identity/Account/Manage/PersonalDatastrony.
Troubleshooting
Jeśli wystąpią problemy:
| Objaw | Prawdopodobna przyczyna | Rozwiązanie |
|---|---|---|
| Pola niestandardowe nie są wyświetlane w formularzu rejestracji | Zapomniano zaktualizować plik Register.cshtml | Upewnij się, że utworzono szkielet i zmodyfikowano Areas/Identity/Pages/Account/Register.cshtml |
| Dane niestandardowe nie są zapisywane | Zapomniałeś dodać migrację lub zaktualizować bazę danych | Uruchom dotnet ef migrations add CustomUserData, a następnie dotnet ef database update |
| Pola niestandardowe nieuwzględnione w pobranych danych | Brak atrybutu [PersonalData] |
Dodawanie [PersonalData] atrybutu do właściwości w klasie użytkownika |
| Błędy kompilacji po wygenerowaniu szkieletu | Niezgodność wersji pakietu | Upewnij się, że wszystkie Identity pakiety są zgodne z wersją platformy docelowej |
Note
Przykłady kodu i instrukcje dotyczące .NET 8 odwołują się do folderu przykładowego 10.x. W tym scenariuszu kod jest identyczny między .NET 8 a .NET 10. Tylko platforma docelowa i wersje pakietów różnią się w pliku projektu.
Szybki start: dodawanie dwóch właściwości niestandardowych
W przypadku prostego scenariusza, w którym chcesz dodać tylko kilka niestandardowych właściwości, takich jak FirstName i LastName, do użytkownika Identity:
- Tworzenie nowego projektu ASP.NET Core z uwierzytelnianiem poszczególnych kont
- Wygeneruj szkielet Identity, aby zastąpić strony Register i Manage/Index
- Dodaj właściwości niestandardowe do klasy użytkownika (np.
FirstName,LastName) z atrybutem[PersonalData] - Zaktualizuj strony Rejestracja oraz Zarządzanie/Indeks, aby uwzględnić pola dla swoich właściwości
- Utwórz i zastosuj migrację
- Test rejestracji i zarządzania profilem
Poniższe sekcje zawierają szczegółowe instrukcje krok po kroku dotyczące tego procesu.
Tworzenie aplikacji internetowej
Możesz utworzyć aplikację internetową Razor Pages lub MVC. Oba używają tego samego IdentityRazor interfejsu użytkownika stron.
Razor Stron
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz Aplikacja internetowa ASP.NET Core>Dalej
- Wybierz Typ uwierzytelniania>Konta indywidualne>Utwórz
- Skompiluj i uruchom projekt.
MVC
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz aplikację internetową ASP.NET Core (model-View-Controller)>Dalej
- Wybierz Typ uwierzytelniania>Konta indywidualne>Utwórz
- Skompiluj i uruchom projekt.
Note
Identity Interfejs użytkownika jest implementowany jako Razor biblioteka klas. Podczas tworzenia projektu MVC z uwierzytelnianiem strony Identity są udostępniane z biblioteki klas Razor, co oznacza, że projekt MVC używa stron Razor do obsługi Identity, mimo że w pozostałej części projektu są używane kontrolery i widoki MVC.
Uruchamianie szkieletu Identity
- Visual Studio
- interfejs wiersza polecenia .NET
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt >Add>Nowy element szkieletowy.
- W lewym panelu okna dialogowego Dodawanie szkieletu wybierz Identity>Dodaj.
- W oknie dialogowym Dodaj Identity są następujące opcje:
- Wybierz istniejący plik układu
~/Pages/Shared/_Layout.cshtmldla stron Razor lub~/Views/Shared/_Layout.cshtmldla MVC - Wybierz następujące pliki do zastąpienia:
- Account/Register
- Account/Manage/Index
- Wybierz przycisk , + aby utworzyć nową klasę kontekstu danych. Zaakceptuj typ (WebApp1.Models.WebApp1Context , jeśli projekt ma nazwę WebApp1).
- Wybierz przycisk , + aby utworzyć nową klasę Użytkownik. Zaakceptuj typ (WebApp1User , jeśli projekt ma nazwę WebApp1) >Dodaj.
- Wybierz istniejący plik układu
- Wybierz Dodaj.
Postępuj zgodnie z instrukcjami w temacie Migracje, Użyj uwierzytelniania i układu, aby wykonać następujące czynności:
- Utwórz migrację i zaktualizuj bazę danych.
- Dodaj
UseAuthenticationdoProgram.cs - Dodaj
<partial name="_LoginPartial" />do pliku układu. - Przetestuj aplikację:
- Rejestrowanie użytkownika
- Wybierz nową nazwę użytkownika (obok linku Wyloguj ). Może być konieczne rozwinięcie okna lub wybranie ikony paska nawigacyjnego, aby wyświetlić nazwę użytkownika i inne linki.
- Wybierz kartę Dane osobowe .
- Wybierz przycisk Pobierz i sprawdź
PersonalData.jsonplik. - Przetestuj przycisk Usuń , który usuwa zalogowanego użytkownika.
Dodawanie niestandardowych danych użytkownika do Identity bazy danych
Zaktualizuj klasę pochodną IdentityUser za pomocą właściwości niestandardowych. Jeśli nazwano projekt WebApp1, plik ma nazwę Areas/Identity/Data/WebApp1User.cs. Zaktualizuj plik przy użyciu następującego kodu:
using Microsoft.AspNetCore.Identity;
namespace WebApp1.Areas.Identity.Data;
public class WebApp1User : IdentityUser
{
[PersonalData]
public string? Name { get; set; }
[PersonalData]
public DateTime DOB { get; set; }
}
Właściwości z atrybutem PersonalData to:
- Usunięto, gdy strona wywołuje metodę
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete. - Dane pobrane i uwzględnione przez stronę
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor.
Account/Manage/Index.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Manage/Index.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class IndexModel : PageModel
{
private readonly UserManager<WebApp1User> _userManager;
private readonly SignInManager<WebApp1User> _signInManager;
public IndexModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public string Username { get; set; }
// Remaining API warnings omited.
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
private async Task LoadAsync(WebApp1User user)
{
var userName = await _userManager.GetUserNameAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Name = user.Name,
DOB = user.DOB,
PhoneNumber = phoneNumber
};
}
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)}'.");
}
await LoadAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with 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 = "Unexpected error when trying to set phone number.";
return RedirectToPage();
}
}
if (Input.Name != user.Name)
{
user.Name = Input.Name;
}
if (Input.DOB != user.DOB)
{
user.DOB = Input.DOB;
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Manage/Index.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
ViewData["ActivePage"] = ManageNavPages.Index;
}
<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="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-floating">
<input asp-for="Username" class="form-control" disabled />
<label asp-for="Username" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.PhoneNumber" class="form-control" />
<label asp-for="Input.PhoneNumber" class="form-label"></label>
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Account/Register.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Register.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class RegisterModel : PageModel
{
private readonly SignInManager<WebApp1User> _signInManager;
private readonly UserManager<WebApp1User> _userManager;
private readonly IUserStore<WebApp1User> _userStore;
private readonly IUserEmailStore<WebApp1User> _emailStore;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<WebApp1User> userManager,
IUserStore<WebApp1User> userStore,
SignInManager<WebApp1User> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_userStore = userStore;
_emailStore = GetEmailStore();
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[BindProperty]
public InputModel Input { get; set; }
// Remaining API warnings omited.
public string ReturnUrl { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
user.Name = Input.Name;
user.DOB = Input.DOB;
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
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>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
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();
}
private WebApp1User CreateUser()
{
try
{
return Activator.CreateInstance<WebApp1User>();
}
catch
{
throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
$"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
$"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
}
}
private IUserEmailStore<WebApp1User> GetEmailStore()
{
if (!_userManager.SupportsUserEmail)
{
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore<WebApp1User>)_userStore;
}
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Register.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB"></label>
<span asp-validation-for="Input.DOB" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-6 col-md-offset-2">
<section>
<h3>Use another service to register.</h3>
<hr />
@{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</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" />
}
Skompiluj projekt.
Aktualizowanie układu
Zobacz Zmiany układu , aby uzyskać instrukcje dotyczące dodawania linków logowania i wylogowyń do każdej strony.
Dodaj migrację dla niestandardowych danych użytkownika
- Visual Studio
- interfejs wiersza polecenia .NET
W konsoli Visual Studio Menedżer pakietów:
Add-Migration CustomUserData
Update-Database
Testowanie tworzenia, wyświetlania, pobierania, usuwania niestandardowych danych użytkownika
Przetestuj aplikację:
- Zarejestruj nowego użytkownika.
- Wyświetl niestandardowe dane użytkownika na
/Identity/Account/Managestronie. - Pobierz i wyświetl dane osobowe użytkowników ze
/Identity/Account/Manage/PersonalDatastrony.
Troubleshooting
Jeśli wystąpią problemy:
| Objaw | Prawdopodobna przyczyna | Rozwiązanie |
|---|---|---|
| Pola niestandardowe nie są wyświetlane w formularzu rejestracji | Zapomniano zaktualizować plik Register.cshtml | Upewnij się, że wygenerowano szkielet i zmodyfikowano Areas/Identity/Pages/Account/Register.cshtml |
| Dane niestandardowe nie są zapisywane | Zapomniałeś dodać migrację lub zaktualizować bazę danych | Uruchom dotnet ef migrations add CustomUserData, a następnie dotnet ef database update |
| Pola niestandardowe niewystępujące w pobranych danych | Brak atrybutu [PersonalData] |
Dodawanie [PersonalData] atrybutu do właściwości w klasie użytkownika |
| Błędy kompilacji po wygenerowaniu szkieletu | Niezgodność wersji pakietu | Upewnij się, że wszystkie Identity pakiety są zgodne z wersją platformy docelowej |
Tworzenie Razor aplikacji internetowej
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz ASP.NET Core aplikację internetową>OK
- Wybierz pozycję Aplikacja> internetowaOK
- Skompiluj i uruchom projekt.
Uruchamianie szkieletu Identity
- Visual Studio
- interfejs wiersza polecenia .NET
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt >Add>Nowy element szkieletowy.
- W lewym panelu okna dialogowego Dodawanie szkieletu wybierz Identity>Dodaj.
- W oknie dialogowym Dodaj Identity są następujące opcje:
- Wybierz istniejący plik układu
~/Pages/Shared/_Layout.cshtml - Wybierz następujące pliki do zastąpienia:
- Account/Register
- Account/Manage/Index
- Wybierz przycisk , + aby utworzyć nową klasę kontekstu danych. Zaakceptuj typ (WebApp1.Models.WebApp1Context , jeśli projekt ma nazwę WebApp1).
- Wybierz przycisk , + aby utworzyć nową klasę Użytkownik. Zaakceptuj typ (WebApp1User , jeśli projekt ma nazwę WebApp1) >Dodaj.
- Wybierz istniejący plik układu
- Wybierz Dodaj.
Postępuj zgodnie z instrukcjami w sekcji Migrations, UseAuthentication i layout, aby wykonać następujące czynności:
- Utwórz migrację i zaktualizuj bazę danych.
- Dodaj
UseAuthenticationdoProgram.cs - Dodaj
<partial name="_LoginPartial" />do pliku układu. - Przetestuj aplikację:
- Rejestrowanie użytkownika
- Wybierz nową nazwę użytkownika (obok linku Wyloguj ). Może być konieczne rozwinięcie okna lub wybranie ikony paska nawigacyjnego, aby wyświetlić nazwę użytkownika i inne linki.
- Wybierz kartę Dane osobowe .
- Wybierz przycisk Pobierz i sprawdź
PersonalData.jsonplik. - Przetestuj przycisk Usuń , który usuwa zalogowanego użytkownika.
Dodawanie niestandardowych danych użytkownika do Identity bazy danych
Zaktualizuj klasę pochodną IdentityUser za pomocą właściwości niestandardowych. Jeśli nazwano projekt WebApp1, plik ma nazwę Areas/Identity/Data/WebApp1User.cs. Zaktualizuj plik przy użyciu następującego kodu:
using Microsoft.AspNetCore.Identity;
namespace WebApp1.Areas.Identity.Data;
public class WebApp1User : IdentityUser
{
[PersonalData]
public string? Name { get; set; }
[PersonalData]
public DateTime DOB { get; set; }
}
Właściwości z atrybutem PersonalData to:
- Usunięto, gdy strona wywołuje metodę
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete. - Dane pobrane i uwzględnione przez stronę
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor.
Account/Manage/Index.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Manage/Index.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class IndexModel : PageModel
{
private readonly UserManager<WebApp1User> _userManager;
private readonly SignInManager<WebApp1User> _signInManager;
public IndexModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public string Username { get; set; }
// Remaining API warnings ommited.
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
private async Task LoadAsync(WebApp1User user)
{
var userName = await _userManager.GetUserNameAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Name = user.Name,
DOB = user.DOB,
PhoneNumber = phoneNumber
};
}
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)}'.");
}
await LoadAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with 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 = "Unexpected error when trying to set phone number.";
return RedirectToPage();
}
}
if (Input.Name != user.Name)
{
user.Name = Input.Name;
}
if (Input.DOB != user.DOB)
{
user.DOB = Input.DOB;
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Manage/Index.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
ViewData["ActivePage"] = ManageNavPages.Index;
}
<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="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-floating">
<input asp-for="Username" class="form-control" disabled />
<label asp-for="Username" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB" class="form-label"></label>
</div>
<div class="form-floating">
<input asp-for="Input.PhoneNumber" class="form-control" />
<label asp-for="Input.PhoneNumber" class="form-label"></label>
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Account/Register.cshtml Aktualizowanie strony
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Register.cshtml.cs za pomocą następującego wyróżnionego kodu:
public class RegisterModel : PageModel
{
private readonly SignInManager<WebApp1User> _signInManager;
private readonly UserManager<WebApp1User> _userManager;
private readonly IUserStore<WebApp1User> _userStore;
private readonly IUserEmailStore<WebApp1User> _emailStore;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<WebApp1User> userManager,
IUserStore<WebApp1User> userStore,
SignInManager<WebApp1User> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_userStore = userStore;
_emailStore = GetEmailStore();
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[BindProperty]
public InputModel Input { get; set; }
// Remaining API warnings ommited.
public string ReturnUrl { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
user.Name = Input.Name;
user.DOB = Input.DOB;
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
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>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
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();
}
private WebApp1User CreateUser()
{
try
{
return Activator.CreateInstance<WebApp1User>();
}
catch
{
throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
$"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
$"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
}
}
private IUserEmailStore<WebApp1User> GetEmailStore()
{
if (!_userManager.SupportsUserEmail)
{
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore<WebApp1User>)_userStore;
}
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Register.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating">
<input asp-for="Input.Name" class="form-control" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.DOB" class="form-control" />
<label asp-for="Input.DOB"></label>
<span asp-validation-for="Input.DOB" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating">
<input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-6 col-md-offset-2">
<section>
<h3>Use another service to register.</h3>
<hr />
@{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</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" />
}
Skompiluj projekt.
Aktualizowanie układu
Zobacz Zmiany układu , aby uzyskać instrukcje dotyczące dodawania linków logowania i wylogowyń do każdej strony.
Dodaj migrację dla niestandardowych danych użytkownika
- Visual Studio
- interfejs wiersza polecenia .NET
W konsoli Visual Studio Menedżer pakietów:
Add-Migration CustomUserData
Update-Database
Testowanie tworzenia, wyświetlania, pobierania, usuwania niestandardowych danych użytkownika
Przetestuj aplikację:
- Zarejestruj nowego użytkownika.
- Wyświetl niestandardowe dane użytkownika na
/Identity/Account/Managestronie. - Pobierz i wyświetl dane osobowe użytkowników ze
/Identity/Account/Manage/PersonalDatastrony.
Tworzenie Razor aplikacji internetowej
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz ASP.NET Core aplikację internetową>OK
- Wybierz pozycję Aplikacja> internetowaOK
- Skompiluj i uruchom projekt.
Uruchamianie szkieletu Identity
- Visual Studio
- interfejs wiersza polecenia .NET
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt >Add>Nowy element szkieletowy.
- W lewym panelu okna dialogowego Dodawanie szkieletu wybierz Identity>Dodaj.
- W oknie dialogowym Dodaj Identity są następujące opcje:
- Wybierz istniejący plik układu
~/Pages/Shared/_Layout.cshtml - Wybierz następujące pliki do zastąpienia:
- Account/Register
- Account/Manage/Index
- Wybierz przycisk , + aby utworzyć nową klasę kontekstu danych. Zaakceptuj typ (WebApp1.Models.WebApp1Context , jeśli projekt ma nazwę WebApp1).
- Wybierz przycisk , + aby utworzyć nową klasę Użytkownik. Zaakceptuj typ (WebApp1User , jeśli projekt ma nazwę WebApp1) >Dodaj.
- Wybierz istniejący plik układu
- Wybierz Dodaj.
Postępuj zgodnie z instrukcjami w sekcji Migrations, UseAuthentication i layout, aby wykonać następujące czynności:
- Utwórz migrację i zaktualizuj bazę danych.
- Dodaj
UseAuthenticationdoStartup.Configure. - Dodaj
<partial name="_LoginPartial" />do pliku układu. - Przetestuj aplikację:
- Rejestrowanie użytkownika
- Wybierz nową nazwę użytkownika (obok linku Wyloguj ). Może być konieczne rozwinięcie okna lub wybranie ikony paska nawigacyjnego, aby wyświetlić nazwę użytkownika i inne linki.
- Wybierz kartę Dane osobowe .
- Wybierz przycisk Pobierz i sprawdź
PersonalData.jsonplik. - Przetestuj przycisk Usuń , który usuwa zalogowanego użytkownika.
Dodawanie niestandardowych danych użytkownika do Identity bazy danych
Zaktualizuj klasę pochodną IdentityUser za pomocą właściwości niestandardowych. Jeśli nazwano projekt WebApp1, plik ma nazwę Areas/Identity/Data/WebApp1User.cs. Zaktualizuj plik przy użyciu następującego kodu:
using System;
using Microsoft.AspNetCore.Identity;
namespace WebApp1.Areas.Identity.Data
{
public class WebApp1User : IdentityUser
{
[PersonalData]
public string Name { get; set; }
[PersonalData]
public DateTime DOB { get; set; }
}
}
Właściwości z atrybutem PersonalData to:
- Usunięto, gdy strona wywołuje metodę
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete. - Dane pobrane i uwzględnione przez stronę
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor.
Aktualizowanie strony Account/Manage/Index.cshtml
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Manage/Index.cshtml.cs za pomocą następującego wyróżnionego kodu:
public partial class IndexModel : PageModel
{
private readonly UserManager<WebApp1User> _userManager;
private readonly SignInManager<WebApp1User> _signInManager;
public IndexModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public string Username { get; set; }
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
private async Task LoadAsync(WebApp1User user)
{
var userName = await _userManager.GetUserNameAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Name = user.Name,
DOB = user.DOB,
PhoneNumber = phoneNumber
};
}
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)}'.");
}
await LoadAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound(
$"Unable to load user with 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)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException(
$"Unexpected error occurred setting phone number for user with ID '{userId}'.");
}
}
if (Input.Name != user.Name)
{
user.Name = Input.Name;
}
if (Input.DOB != user.DOB)
{
user.DOB = Input.DOB;
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Manage/Index.cshtml za pomocą następującego wyróżnionego znacznika:
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
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="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.Name"></label>
<input asp-for="Input.Name" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Input.DOB"></label>
<input asp-for="Input.DOB" class="form-control" />
</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>
<button id="update-profile-button" type="submit"
class="btn btn-primary">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Aktualizowanie strony Account/Register.cshtml
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Register.cshtml.cs za pomocą następującego wyróżnionego kodu:
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<WebApp1User> _signInManager;
private readonly UserManager<WebApp1User> _userManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> 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 IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new WebApp1User {
Name = Input.Name,
DOB = Input.DOB,
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);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", 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>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
}
else
{
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();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Register.cshtml za pomocą następującego wyróżnionego znacznika:
@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>
<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.DOB"></label>
<input asp-for="Input.DOB" class="form-control" />
<span asp-validation-for="Input.DOB" class="text-danger"></span>
</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">
<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 class="col-md-6 col-md-offset-2">
<section>
<h4>Use another service to register.</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" />
}
Skompiluj projekt.
Dodaj migrację dla niestandardowych danych użytkownika
- Visual Studio
- interfejs wiersza polecenia .NET
W konsoli Visual Studio Menedżer pakietów:
Add-Migration CustomUserData
Update-Database
Testowanie tworzenia, wyświetlania, pobierania, usuwania niestandardowych danych użytkownika
Przetestuj aplikację:
- Zarejestruj nowego użytkownika.
- Wyświetl niestandardowe dane użytkownika na
/Identity/Account/Managestronie. - Pobierz i wyświetl dane osobowe użytkowników ze
/Identity/Account/Manage/PersonalDatastrony.
Dodaj oświadczenia do Identity używając IUserClaimsPrincipalFactory<ApplicationUser>
Note
Ta sekcja nie jest rozszerzeniem poprzedniego samouczka. Aby zastosować poniższe kroki do aplikacji utworzonej przy użyciu samouczka, zobacz this GitHub problem.
Dodatkowe oświadczenia można dodać do ASP.NET Core Identity przy użyciu interfejsu IUserClaimsPrincipalFactory<T>. Tę klasę można dodać do aplikacji w metodzie Startup.ConfigureServices . Dodaj niestandardową implementację klasy w następujący sposób:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>,
AdditionalUserClaimsPrincipalFactory>();
Kod demonstracyjny używa klasy ApplicationUser. Ta klasa dodaje właściwość IsAdmin, która jest wykorzystywana do dodania dodatkowego oświadczenia.
public class ApplicationUser : IdentityUser
{
public bool IsAdmin { get; set; }
}
Element AdditionalUserClaimsPrincipalFactory implementuje UserClaimsPrincipalFactory interfejs. Nowe oświadczenie roli jest dodawane do elementu ClaimsPrincipal.
public class AdditionalUserClaimsPrincipalFactory
: UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public AdditionalUserClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{}
public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
var claims = new List<Claim>();
if (user.IsAdmin)
{
claims.Add(new Claim(JwtClaimTypes.Role, "admin"));
}
else
{
claims.Add(new Claim(JwtClaimTypes.Role, "user"));
}
identity.AddClaims(claims);
return principal;
}
}
Następnie można użyć dodatkowego oświadczenia w aplikacji. Na stronie Razor można użyć instancji IAuthorizationService do uzyskania dostępu do wartości roszczenia.
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService
@if ((await AuthorizationService.AuthorizeAsync(User, "IsAdmin")).Succeeded)
{
<ul class="mr-auto navbar-nav">
<li class="nav-item">
<a class="nav-link" asp-controller="Admin" asp-action="Index">ADMIN</a>
</li>
</ul>
}
.NET Core 2.2 lub późniejsza wersja SDK
Tworzenie Razor aplikacji internetowej
- Visual Studio
- interfejs wiersza polecenia .NET
- Z menu Visual Studio File wybierz Nowy>Project. Nadaj projektowi nazwę WebApp1 , jeśli ma być zgodna z przestrzenią nazw przykładowego kodu pobierania .
- Wybierz ASP.NET Core aplikację internetową>OK
- Wybierz ASP.NET Core 2.2 na liście rozwijanej
- Wybierz pozycję Aplikacja> internetowaOK
- Skompiluj i uruchom projekt.
Uruchamianie szkieletu Identity
- Visual Studio
- interfejs wiersza polecenia .NET
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt >Add>Nowy element szkieletowy.
- W lewym panelu okna dialogowego Dodawanie szkieletu wybierz Identity>Dodaj.
- W oknie dialogowym Dodaj Identity są następujące opcje:
- Wybierz istniejący plik układu
~/Pages/Shared/_Layout.cshtml - Wybierz następujące pliki do zastąpienia:
- Account/Register
- Account/Manage/Index
- Wybierz przycisk , + aby utworzyć nową klasę kontekstu danych. Zaakceptuj typ (WebApp1.Models.WebApp1Context , jeśli projekt ma nazwę WebApp1).
- Wybierz przycisk , + aby utworzyć nową klasę Użytkownik. Zaakceptuj typ (WebApp1User , jeśli projekt ma nazwę WebApp1) >Dodaj.
- Wybierz istniejący plik układu
- Wybierz Dodaj.
Postępuj zgodnie z instrukcjami w sekcji Migrations, UseAuthentication, and layout, aby wykonać następujące czynności:
- Utwórz migrację i zaktualizuj bazę danych.
- Dodaj
UseAuthenticationdoStartup.Configure. - Dodaj
<partial name="_LoginPartial" />do pliku układu. - Przetestuj aplikację:
- Rejestrowanie użytkownika
- Wybierz nową nazwę użytkownika (obok linku Wyloguj ). Może być konieczne rozwinięcie okna lub wybranie ikony paska nawigacyjnego, aby wyświetlić nazwę użytkownika i inne linki.
- Wybierz kartę Dane osobowe .
- Wybierz przycisk Pobierz i sprawdź
PersonalData.jsonplik. - Przetestuj przycisk Usuń , który usuwa zalogowanego użytkownika.
Dodawanie niestandardowych danych użytkownika do Identity bazy danych
Zaktualizuj klasę pochodną IdentityUser za pomocą właściwości niestandardowych. Jeśli nazwano projekt WebApp1, plik ma nazwę Areas/Identity/Data/WebApp1User.cs. Zaktualizuj plik przy użyciu następującego kodu:
using Microsoft.AspNetCore.Identity;
using System;
namespace WebApp1.Areas.Identity.Data
{
public class WebApp1User : IdentityUser
{
[PersonalData]
public string Name { get; set; }
[PersonalData]
public DateTime DOB { get; set; }
}
}
Właściwości z atrybutem PersonalData to:
- Usunięto, gdy strona wywołuje metodę
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete. - Dane pobrane i uwzględnione przez stronę
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor.
Aktualizowanie strony Account/Manage/Index.cshtml
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Manage/Index.cshtml.cs za pomocą następującego wyróżnionego kodu:
public partial class IndexModel : PageModel
{
private readonly UserManager<WebApp1User> _userManager;
private readonly SignInManager<WebApp1User> _signInManager;
private readonly IEmailSender _emailSender;
public IndexModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> 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
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Phone]
[Display(Name = "Phone number")]
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
{
Name = user.Name,
DOB = user.DOB,
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}'.");
}
}
if (Input.Name != user.Name)
{
user.Name = Input.Name;
}
if (Input.DOB != user.DOB)
{
user.DOB = Input.DOB;
}
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}'.");
}
}
await _userManager.UpdateAsync(user);
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();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Manage/Index.cshtml za pomocą następującego wyróżnionego znacznika:
@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">
<label asp-for="Input.Name"></label>
<input asp-for="Input.Name" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Input.DOB"></label>
<input asp-for="Input.DOB" class="form-control" />
</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>
<button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Aktualizowanie strony Account/Register.cshtml
Zaktualizuj element InputModel w Areas/Identity/Pages/Account/Register.cshtml.cs za pomocą następującego wyróżnionego kodu:
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<WebApp1User> _signInManager;
private readonly UserManager<WebApp1User> _userManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<WebApp1User> userManager,
SignInManager<WebApp1User> 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
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Full name")]
public string Name { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[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 = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[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 WebApp1User {
Name = Input.Name,
DOB = Input.DOB,
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();
}
}
Zaktualizuj element Areas/Identity/Pages/Account/Register.cshtml za pomocą następującego wyróżnionego znacznika:
@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>
<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.DOB"></label>
<input asp-for="Input.DOB" class="form-control" />
<span asp-validation-for="Input.DOB" class="text-danger"></span>
</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">
<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" />
}
Skompiluj projekt.
Dodaj migrację dla niestandardowych danych użytkownika
Właściwości niestandardowe dodane do WebApp1UserIdentity klasy użytkownika muszą zostać odzwierciedlone w bazie danych. Migracja jest teraz potrzebna do wygenerowania bazy danych SQL wymaganej do dodania nowych kolumn, a następnie należy zaktualizować bazę danych, aby zastosować te zmiany.
Szkielet Identity utworzył nowy DbContext (WebApp1Context) wraz z oryginałem ApplicationDbContext z szablonu projektu. Podczas uruchamiania poleceń migracji należy określić nowy WebApp1ContextIdentityDbContext element do użycia.
- Visual Studio
- interfejs wiersza polecenia .NET
W konsoli Visual Studio Menedżer pakietów:
Add-Migration CustomUserData -Context WebApp1Context
Update-Database -Context WebApp1Context
Testowanie tworzenia, wyświetlania, pobierania, usuwania niestandardowych danych użytkownika
Przetestuj aplikację:
- Zarejestruj nowego użytkownika.
- Wyświetl niestandardowe dane użytkownika na
/Identity/Account/Managestronie. - Pobierz i wyświetl dane osobowe użytkowników ze
/Identity/Account/Manage/PersonalDatastrony.