Cvičení – přizpůsobení architektury Identity
Architektura Identity standardně reprezentuje uživatele pomocí třídy IdentityUser. Jeden ze způsobů, jak rozšířit data zaznamenaná během registrace, spočívá ve vytvoření třídy odvozené od IdentityUser. V této lekci se vytvoří odvozená třída s názvem ContosoPetsUser. ContosoPetsUser bude obsahovat vlastnosti pro uložení jména a příjmení uživatele.

Ke shromáždění dalších informací o profilu uživatele se také vyžadují změny uživatelského rozhraní. Následující postup vysvětluje postup shromáždění jména a příjmení registrovaného uživatele.
Přizpůsobení dat uživatelského účtu
Přidejte do projektu registrační soubory uživatele, které mají být upraveny:
dotnet aspnet-codegenerator identity \ --dbContext ContosoPetsAuth \ --files "Account.Manage.EnableAuthenticator;Account.Manage.Index;Account.Register" \ --userClass ContosoPetsUser \ --forceV předcházejícím příkazu:
- Parametr
--dbContextinformuje nástroj o existující tříděContosoPetsAuthodvozené od třídyDbContext. - Parametr
--filesurčuje středníkem oddělený seznam jedinečných souborů, které se mají přidat do oblasti Identity. - Parametr
--userClasszpůsobí vytvoření třídy s názvemContosoPetsUserodvozené od třídyIdentityUser. - Parametr
--forcezpůsobí přepsání existujících souborů v oblasti Identity.
Tip
Spuštěním následujícího příkazu z kořenového adresáře projektu zobrazíte platné hodnoty parametru
--files:dotnet aspnet-codegenerator identity --listFilesDo adresáře Areas/Identity jsou přidány následující soubory:
- Data/
- ContosoPetsUser.cs
- Pages/
- _ViewImports.cshtml
- Account/
- _ViewImports.cshtml
- Register.cshtml
- Register.cshtml.cs
- Manage/
- _ManageNav.cshtml
- _ViewImports.cshtml
- EnableAuthenticator.cshtml
- EnableAuthenticator.cshtml.cs
- Index.cshtml
- Index.cshtml.cs
- ManageNavPages.cs
Soubor Data/ContosoPetsAuth.cs, který existoval před spuštěním předchozího příkazu, byl navíc přepsán, protože byl použit parametr
--force. Deklarace třídyContosoPetsAuthteď odkazuje na nově vytvořený typ uživateleContosoPetsUser:public class ContosoPetsAuth : IdentityDbContext<ContosoPetsUser>Byl vygenerován kód stránky Razor EnableAuthenticator, který bude upraven později v tomto modulu.
- Parametr
V metodě
Configuresouboru Areas/Identity/IdentityHostingStartup.cs musí být voláníAddDefaultIdentityinformováno o novém typu uživatele architektury Identity. Začleňte následující zvýrazněnou změnu a soubor uložte.services.AddDefaultIdentity<ContosoPetsUser>() .AddDefaultUI() .AddEntityFrameworkStores<ContosoPetsAuth>();Aktualizací souboru Pages/Shared/_LoginPartial.cshtml začleňte následující zvýrazněné změny. Uložte provedené změny.
@using Microsoft.AspNetCore.Identity @using ContosoPets.Ui.Areas.Identity.Data @inject SignInManager<ContosoPetsUser> SignInManager @inject UserManager<ContosoPetsUser> UserManager <ul class="navbar-nav">Předchozí změny aktualizují typ uživatele předaný do
SignInManager<T>aUserManager<T>v direktivách@inject. Místo výchozího typuIdentityUserse teď odkazuje na uživateleContosoPetsUser. Kvůli překladu odkazůContosoPetsUserbyla přidána direktiva@using.Soubor Pages/Shared/_LoginPartial.cshtml je fyzicky umístěný mimo oblast Identity. V důsledku toho nebyl tento soubor aktualizován automaticky pomocí nástroje pro generování kódu. Příslušné změny musely být provedeny ručně.
Tip
Jako alternativu k ruční úpravě souboru _LoginPartial.cshtml je možné ho před spuštěním nástroje pro generování kódu odstranit. Soubor _LoginPartial.cshtml se vytvoří znovu s odkazy na novou třídu
ContosoPetsUser.Aktualizujte soubor Areas/Identity/Data/ContosoPetsUser.cs tak, aby podporoval ukládání a načítání dalších dat profilu uživatele. Proveďte následující změny:
Přidejte vlastnosti
FirstNameaLastName:public class ContosoPetsUser : IdentityUser { [Required] [MaxLength(100)] public string FirstName { get; set; } [Required] [MaxLength(100)] public string LastName { get; set; } }Vlastnosti v předchozím fragmentu kódu představují další sloupce, které se mají vytvořit v podkladové tabulce
AspNetUsers. Obě vlastnosti jsou povinné a jsou proto opatřené atributem[Required]. Atribut[Required]rovněž nastavuje omezení, že sloupec v podkladové databázová tabulce nesmí obsahovat hodnotu null. Atribut[MaxLength]navíc udává, že maximální povolená délka je 100 znaků. Datový typ sloupce podkladové tabulky je definován odpovídajícím způsobem.Na začátek souboru přidejte následující příkaz
using. Uložte provedené změny.using System.ComponentModel.DataAnnotations;Předchozí kód přeloží atributy datových poznámek použitých na vlastnosti
FirstNameaLastName.
Aktualizace databáze
Vytvořením a použitím migrace EF Core aktualizujte podkladové úložiště dat:
dotnet ef migrations add UpdateUser && \ dotnet ef database updatePři migraci EF Core
UpdateUserse na schéma tabulkyAspNetUserspoužil změnový skript DDL. Konkrétně byly přidány sloupceFirstNameaLastName, jak je vidět na následujícím výňatku z výstupu migrace:info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1,005ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] ALTER TABLE "AspNetUsers" ADD "FirstName" character varying(100) NOT NULL DEFAULT ''; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (517ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] ALTER TABLE "AspNetUsers" ADD "LastName" character varying(100) NOT NULL DEFAULT '';info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (37ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] ALTER TABLE [AspNetUsers] ADD [FirstName] nvarchar(100) NOT NULL DEFAULT N''; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (36ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] ALTER TABLE [AspNetUsers] ADD [LastName] nvarchar(100) NOT NULL DEFAULT N'';Provedením následujících kroků analyzujte dopad migrace EF Core
UpdateUserna schéma tabulkyAspNetUsers. Budete tak mít přehled o dopadu, jaký má rozšíření datového modelu Identity na podkladové úložiště dat.
Spuštěním následujícího příkazu zobrazte schéma tabulky:
db -c '\d "AspNetUsers"'Zobrazí se následující výstup:
Table "public.AspNetUsers" Column | Type | Collation | Nullable | Default ----------------------+--------------------------+-----------+----------+----------------------- Id | text | | not null | UserName | character varying(256) | | | NormalizedUserName | character varying(256) | | | Email | character varying(256) | | | NormalizedEmail | character varying(256) | | | EmailConfirmed | boolean | | not null | PasswordHash | text | | | SecurityStamp | text | | | ConcurrencyStamp | text | | | PhoneNumber | text | | | PhoneNumberConfirmed | boolean | | not null | TwoFactorEnabled | boolean | | not null | LockoutEnd | timestamp with time zone | | | LockoutEnabled | boolean | | not null | AccessFailedCount | integer | | not null | FirstName | character varying(100) | | not null | ''::character varying LastName | character varying(100) | | not null | ''::character varyingVlastnosti
FirstNameaLastNameve tříděContosoPetsUserodpovídají sloupcůmFirstNameaLastNamev předchozím výstupu. Kvůli atributům[MaxLength(100)]byl k oběma sloupcům přiřazen datový typcharacter varying(100). Kvůli atributům[Required]bylo přidáno omezení, že nesmí být použita hodnota null.Posuňte se v příkazovém prostředí dolů, dokud se nezobrazí následující informace o indexu:
Indexes: "PK_AspNetUsers" PRIMARY KEY, btree ("Id") "UserNameIndex" UNIQUE, btree ("NormalizedUserName") "EmailIndex" btree ("NormalizedEmail")Index
PK_AspNetUsersukazuje, že sloupecIdje jedinečným identifikátorem uživatelského účtu.Stisknutím klávesy q ukončete prohlížeč textu v příkazovém prostředí.
Spuštěním následujícího příkazu zobrazte schéma tabulky:
db -Q "SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH AS MAX_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='AspNetUsers'" -Y 20Zobrazí se následující výstup:
COLUMN_NAME IS_NULLABLE DATA_TYPE MAX_LENGTH -------------------- ----------- -------------------- ----------- Id NO nvarchar 450 UserName YES nvarchar 256 NormalizedUserName YES nvarchar 256 Email YES nvarchar 256 NormalizedEmail YES nvarchar 256 EmailConfirmed NO bit NULL PasswordHash YES nvarchar -1 SecurityStamp YES nvarchar -1 ConcurrencyStamp YES nvarchar -1 PhoneNumber YES nvarchar -1 PhoneNumberConfirmed NO bit NULL TwoFactorEnabled NO bit NULL LockoutEnd YES datetimeoffset NULL LockoutEnabled NO bit NULL AccessFailedCount NO int NULL FirstName NO nvarchar 100 LastName NO nvarchar 100Vlastnosti
FirstNameaLastNameve tříděContosoPetsUserodpovídají sloupcůmFirstNameaLastNamev předchozím výstupu. Kvůli atributům[MaxLength(100)]byl k oběma sloupcům přiřazen datový typnvarchar(100). Kvůli atributům[Required]bylo přidáno omezení, že nesmí být použita hodnota null. U existujících řádků jsou v nových sloupcích prázdné řetězce.Spuštěním následujícího příkazu zobrazte primární klíč tabulky:
db -i $setupWorkingDirectory/list-aspnetusers-pk.sql -Y 15Následující výstup ukazuje, že sloupec
Idje jedinečným identifikátorem uživatelského účtu:Table Column Primary key --------------- --------------- --------------- AspNetUsers Id PK_AspNetUsers
Přizpůsobení formuláře pro registraci uživatele
V souboru Areas/Identity/Pages/Account/Register.cshtml přidejte následující zvýrazněný kód:
<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.FirstName"></label> <input asp-for="Input.FirstName" class="form-control" /> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.LastName"></label> <input asp-for="Input.LastName" class="form-control" /> <span asp-validation-for="Input.LastName" 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>V předchozím kódu jsou do formuláře pro registraci uživatele přidána textová pole pro jméno a příjmení.
V souboru Areas/Identity/Pages/Account/Register.cshtml.cs přidejte podporu pro textová pole se jménem.
Do vnořené třídy
InputModelpřidejte vlastnostiFirstNameaLastName:public class InputModel { [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "First name")] public string FirstName { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "Last name")] public string LastName { 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; } }Atributy
[Display]definují text popisku, který má být přidružen k těmto textovým polím.Úpravou metody
OnPostAsyncnastavte vlastnostiFirstNameaLastNameu objektuContosoPetsUser. Proveďte následující zvýrazněné změny:public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) { var user = new ContosoPetsUser { FirstName = Input.FirstName, LastName = Input.LastName, UserName = Input.Email, Email = Input.Email, }; var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) {Předchozí změna nastaví vlastnosti
FirstNameaLastNamena zadání uživatele z registračního formuláře.
Přizpůsobení záhlaví webu
Aktualizací souboru Pages/Shared/_LoginPartial.cshtml zobrazte jméno a příjmení shromážděné během registrace uživatele. Jsou zapotřebí zvýrazněné řádky v následujícím fragmentu kódu:
@using Microsoft.AspNetCore.Identity
@using ContosoPets.Ui.Areas.Identity.Data
@inject SignInManager<ContosoPetsUser> SignInManager
@inject UserManager<ContosoPetsUser> UserManager
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
ContosoPetsUser user = await UserManager.GetUserAsync(User);
var fullName = $"{user.FirstName} {user.LastName}";
<li class="nav-item">
<a id="manage" class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello, @fullName!</a>
</li>
<li class="nav-item">
<form id="logoutForm" class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })">
<button id="logout" type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" id="register" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" id="login" asp-area="Identity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>
Přizpůsobení formuláře pro správu profilu
V souboru Areas/Identity/Pages/Account/Manage/Index.cshtml přidejte následující zvýrazněný kód. Uložte provedené změny.
<form id="profile-form" method="post"> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <label asp-for="Input.FirstName"></label> <input asp-for="Input.FirstName" class="form-control" /> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.LastName"></label> <input asp-for="Input.LastName" class="form-control" /> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Username"></label> <input asp-for="Username" class="form-control" disabled /> </div>V souboru Areas/Identity/Pages/Account/Manage/Index.cshtml.cs proveďte následující změny pro podporu textových polí se jménem.
Do vnořené třídy
InputModelpřidejte vlastnostiFirstNameaLastName:public class InputModel { [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "First name")] public string FirstName { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "Last name")] public string LastName { get; set; } [Phone] [Display(Name = "Phone number")] public string PhoneNumber { get; set; } }Do metody
LoadAsynczačleňte zvýrazněné změny:private async Task LoadAsync(ContosoPetsUser user) { var userName = await _userManager.GetUserNameAsync(user); var phoneNumber = await _userManager.GetPhoneNumberAsync(user); Username = userName; Input = new InputModel { PhoneNumber = phoneNumber, FirstName = user.FirstName, LastName = user.LastName, }; }Předchozí kód umožňuje načtení jména a příjmení, které se zobrazí v odpovídajících textových polích formuláře pro správu profilu.
Do metody
OnPostAsynczačleňte zvýrazněné změny. Uložte provedené změny.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(); } user.FirstName = Input.FirstName; user.LastName = Input.LastName; await _userManager.UpdateAsync(user); 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 _signInManager.RefreshSignInAsync(user); StatusMessage = "Your profile has been updated"; return RedirectToPage(); }Předchozí kód umožňuje aktualizaci jména a příjmení v tabulce databáze
AspNetUsers.
Sestavení, nasazení a otestování
Spuštěním následujícího příkazu sestavte aplikaci:
dotnet build --no-restoreParametr
--no-restoreje použitý, protože od posledního sestavení nebyly přidány žádné balíčky NuGet. Proces sestavení vynechá obnovení balíčků NuGet a úspěšně se dokončí bez upozornění. Pokud se sestavení nezdaří, zkontrolujte výstupní informace o odstraňování potíží.Spuštěním následujícího příkazu nasaďte aplikaci do Azure App Service:
az webapp upV prohlížeči přejděte na aplikaci. Pokud jste pořád přihlášení, vyberte možnost odhlášení.
Tip
Pokud potřebujete adresu URL vaší aplikace, zobrazíte ji následujícím příkazem:
echo $webAppUrlVyberte možnost registrace a pomocí aktualizovaného formuláře zaregistrujte nového uživatele.
Poznámka
V ověřovacích omezeních u polí pro jméno a příjmení jsou promítnuty datové poznámky vlastností
FirstNameaLastNameproInputModel.Po registraci jste přesměrováni na domovskou stránku. Záhlaví aplikace teď obsahuje Hello, [jméno] [příjmení]!.
Spuštěním následujícího příkazu potvrďte, že jméno a příjmení jsou uložené v databázi:
db -c 'SELECT "UserName", "Email", "FirstName", "LastName" FROM "AspNetUsers"'Zobrazí se varianta následujícího výstupu:
UserName | Email | FirstName | LastName ---------------------------+---------------------------+-----------+---------- kai.klein@contoso.com | kai.klein@contoso.com | | jana.heinrich@contoso.com | jana.heinrich@contoso.com | Jana | Heinrich (2 rows)db -Q "SELECT UserName, Email, FirstName, LastName FROM dbo.AspNetUsers" -Y 25Zobrazí se varianta následujícího výstupu:
UserName Email FirstName LastName ------------------------- ------------------------- ------------------------- ------------------------- kai.klein@contoso.com kai.klein@contoso.com jana.heinrich@contoso.com jana.heinrich@contoso.com Jana HeinrichPrvní uživatel je zaregistrovaný před přidáním polí
FirstNameaLastNamedo schématu. Následkem toho neobsahuje přidružený záznam tabulkyAspNetUsersv těchto sloupcích žádná data.
Otestování změn ve formuláři pro správu profilu
Ve webové aplikaci se přihlaste pomocí prvního uživatele, kterého jste vytvořili.
Kliknutím na odkaz Hello, ! přejděte na formulář pro správu profilu.
Poznámka
Odkaz se nezobrazuje správně, protože řádek tabulky
AspNetUserspro tohoto uživatele neobsahuje hodnoty políFirstNameaLastName.Zadejte platné hodnoty pro jméno a příjmení. Vyberte Uložit.
Záhlaví aplikace se aktualizuje na Hello, [jméno] [příjmení]!.
Potřebujete pomoc? Projděte si našeho průvodce odstraňováním potíží nebo nahlaste potíže a uveďte konkrétní připomínky.