Exercice - Personnaliser Identity
Dans l’unité précédente, vous avez appris comment fonctionne la personnalisation dans ASP.NET Core Identity. Dans cette unité, vous étendez le modèle de données Identity et apportez les modifications correspondantes dans l’interface utilisateur.
Personnaliser l’interface utilisateur du compte d’utilisateur
Dans cette section, vous allez créer et personnaliser les fichiers de l’interface utilisateur d’Identity pour les utiliser à la place de la bibliothèque de classes Razor par défaut.
Ajoutez les fichiers d’inscription utilisateur à modifier dans le projet :
dotnet aspnet-codegenerator identity --dbContext RazorPagesPizzaAuth --files "Account.Manage.EnableAuthenticator;Account.Manage.Index;Account.Register;Account.ConfirmEmail"
Dans la commande précédente :
- L’option
--dbContext
permet à l’outil de prendre connaissance de la classe dérivée deDbContext
existante appeléeRazorPagesPizzaAuth
. - L’option
--files
spécifie une liste de fichiers uniques séparés par des points-virgules à ajouter à la zone Identity.Account.Manage.Index
est la page de gestion des profils. Cette page est modifiée plus loin dans cette unité.Account.Register
est la page d’enregistrement de l’utilisateur(-trice). Cette page est également modifiée dans cette unité.Account.Manage.EnableAuthenticator
etAccount.ConfirmEmail
sont générés automatiquement, mais ne sont pas modifiés dans cette unité.
Conseil
Exécutez la commande suivante à partir de la racine du projet pour voir les valeurs valides pour l’option
--files
:dotnet aspnet-codegenerator identity --listFiles
Les fichiers suivants sont ajoutés au répertoire Areas/Identity :
- Pages/
- _ViewImports.cshtml
- Account/
- _ViewImports.cshtml
- ConfirmEmail.cshtml
- ConfirmEmail.cshtml.cs
- Register.cshtml
- Register.cshtml.cs
- Manage/
- _ManageNav.cshtml
- _ViewImports.cshtml
- EnableAuthenticator.cshtml
- EnableAuthenticator.cshtml.cs
- Index.cshtml
- Index.cshtml.cs
- ManageNavPages.cs
- L’option
Étendre IdentityUser
On vous donne une nouvelle exigence pour stocker les noms de vos utilisateurs. Étant donné que la classe IdentityUser
par défaut ne contient pas de propriétés pour les prénoms et les noms, vous devez étendre la classe RazorPagesPizzaUser
.
Dans Areas/Identity/Data/RazorPagesPizzaUser.cs, effectuez les changements suivants :
Ajoutez les propriétés
FirstName
etLastName
:using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Identity; namespace RazorPagesPizza.Areas.Identity.Data; public class RazorPagesPizzaUser : IdentityUser { [Required] [MaxLength(100)] public string FirstName { get; set; } = string.Empty; [Required] [MaxLength(100)] public string LastName { get; set; } = string.Empty; }
Les propriétés de l’extrait de code précédent représentent les colonnes supplémentaires à créer dans la table
AspNetUsers
sous-jacente. Les deux propriétés sont obligatoires et sont donc annotées avec l’attribut[Required]
. De plus, l’attribut[MaxLength]
indique qu’une longueur maximale de 100 caractères est autorisée. Le type de données de la colonne de la table sous-jacente est défini en conséquence. Une valeur par défautstring.Empty
est affectée, car le contexte nullable est activé dans ce projet et les propriétés sont des chaînes non nullables.Ajoutez l’instruction
using
suivante au début du fichier.using System.ComponentModel.DataAnnotations;
Le code précédent résout les attributs d’annotation de données appliqués aux propriétés
FirstName
etLastName
.
Mettre à jour la base de données
Maintenant que les modifications du modèle ont été apportées, les changements associés doivent être effectués sur la base de données.
Vérifiez que tous vos changements sont enregistrés.
Créez et appliquez une migration EF Core pour mettre à jour le magasin de données sous-jacent :
dotnet ef migrations add UpdateUser dotnet ef database update
La migration EF Core
UpdateUser
a appliqué un script de changement de DDL au schéma de la tableAspNetUsers
. Plus précisément, les colonnesFirstName
etLastName
ont été ajoutées, comme indiqué dans l’extrait de sortie de la migration suivant :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'';
Examinez la base de données pour analyser l’effet de la
UpdateUser
migration EF CoreAspNetUsers
sur le schéma de la table.Dans le volet SQL Server, développez le nœud Colonnes dans la table dbo.AspNetUsers.
Les propriétés
FirstName
etLastName
de la classeRazorPagesPizzaUser
correspondent aux colonnesFirstName
etLastName
de l’image précédente. Un type de données denvarchar(100)
a été assigné à chacune des deux colonnes en raison des attributs[MaxLength(100)]
. La contrainte non null a été ajoutée parce queFirstName
etLastName
dans la classe sont des chaînes non nullables. Les lignes existantes montrent des chaînes vides dans les nouvelles colonnes.
Personnaliser le formulaire d’inscription d’utilisateur
Vous avez ajouté de nouvelles colonnes pour FirstName
et LastName
. Vous devez maintenant modifier l’interface utilisateur pour afficher les champs correspondants sur le formulaire d’inscription.
Dans Areas/Identity/Pages/Account/Register.cshtml, ajoutez le balisage en surbrillance suivant :
<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" role="alert"></div> <div class="form-floating mb-3"> <input asp-for="Input.FirstName" class="form-control" /> <label asp-for="Input.FirstName"></label> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Input.LastName" class="form-control" /> <label asp-for="Input.LastName"></label> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" /> <label asp-for="Input.Email">Email</label> <span asp-validation-for="Input.Email" class="text-danger"></span> </div>
Avec le balisage précédent, les zones de texte Prénom et Nom sont ajoutées au formulaire d’inscription d’utilisateur.
Dans Areas/Identity/Pages/Account/Register.cshtml.cs, ajoutez une prise en charge pour les zones de texte des noms.
Ajoutez les propriétés
FirstName
etLastName
dans la classe imbriquéeInputModel
: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; } /// <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> [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; }
Les attributs
[Display]
définissent le texte de l’étiquette à associer aux zones de texte.Modifiez la méthode
OnPostAsync
pour définir les propriétésFirstName
etLastName
sur l’objetRazorPagesPizza
. Ajoutez les lignes en surbrillance suivantes :public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl ??= Url.Content("~/"); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { var user = CreateUser(); user.FirstName = Input.FirstName; user.LastName = Input.LastName; await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); var result = await _userManager.CreateAsync(user, Input.Password);
Le changement précédent définit les propriétés
FirstName
etLastName
sur l’entrée utilisateur du formulaire d’inscription.
Personnaliser l’en-tête de site
Mettez à jour Pages/Shared/_LoginPartial.cshtml pour afficher le nom et le prénom collectés pendant l’inscription de l’utilisateur. Les lignes en surbrillance dans l’extrait de code suivant sont nécessaires :
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
RazorPagesPizzaUser? 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>
UserManager.GetUserAsync(User)
retourne un objet RazorPagesPizzaUser
pouvant accepter la valeur Null. L’opérateur ?.
conditionnel nul est utilisé pour accéder aux propriétés FirstName
et LastName
uniquement si l’objet RazorPagesPizzaUser
n’est pas nul.
Personnaliser le formulaire de gestion des profils
Vous avez ajouté les nouveaux champs au formulaire d’inscription utilisateur, mais vous devez également les ajouter au formulaire de gestion des profils afin que les utilisateurs existants puissent les modifier.
Dans Areas/Identity/Pages/Account/Manage/Index.cshtml, ajoutez le balisage en surbrillance suivant. Enregistrez vos modifications.
<form id="profile-form" method="post"> <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div> <div class="form-floating mb-3"> <input asp-for="Input.FirstName" class="form-control" /> <label asp-for="Input.FirstName"></label> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Input.LastName" class="form-control" /> <label asp-for="Input.LastName"></label> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Username" class="form-control" disabled /> <label asp-for="Username" class="form-label"></label> </div>
Dans Areas/Identity/Pages/Account/Manage/Index.cshtml.cs, apportez les changements suivants pour prendre en charge les zones de texte des noms.
Ajoutez les propriétés
FirstName
etLastName
dans la classe imbriquéeInputModel
: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; } /// <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> [Phone] [Display(Name = "Phone number")] public string PhoneNumber { get; set; } }
Incorporez les changements en surbrillance dans la méthode
LoadAsync
:private async Task LoadAsync(RazorPagesPizzaUser 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 }; }
Le code précédent prend en charge la récupération du prénom et du nom pour les afficher dans les zones de texte correspondantes du formulaire de gestion des profils.
Incorporez les changements en surbrillance dans la méthode
OnPostAsync
. Enregistrez vos changements.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) { StatusMessage = "Unexpected error when trying to set phone number."; return RedirectToPage(); } } await _signInManager.RefreshSignInAsync(user); StatusMessage = "Your profile has been updated"; return RedirectToPage(); }
Le code précédent prend en charge la mise à jour du prénom et du nom dans la table
AspNetUsers
de la base de données.
Configurer l’expéditeur de l’e-mail de confirmation
La première fois que vous avez testé l’application, vous avez inscrit un utilisateur, puis cliqué sur un lien pour simuler la confirmation de l’adresse e-mail de l’utilisateur(-trice). Pour envoyer un véritable courriel de confirmation, vous devez créer une implémentation de IEmailSender et l’enregistrer dans le système d’injection de dépendances. Pour simplifier les choses, votre implémentation dans cette unité n’envoie pas réellement d’e-mail à un serveur SMTP (Simple Mail Transfer Protocol). Elle écrit juste le contenu de l’e-mail dans la console.
Étant donné que vous allez afficher l’e-mail en texte brut dans la console, vous devez modifier le message généré pour exclure le texte codé en HTML. Dans Areas/Identity/Pages/Account/Register.cshtml.cs, recherchez le code suivant :
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
Remplacez-le par :
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by visiting the following URL:\r\n\r\n{callbackUrl}");
Dans le volet Explorateur, cliquez avec le bouton droit sur le dossier RazorPagesPizza\Services et créez un fichier appelé EmailSender.cs. Ouvrez le fichier et ajoutez le code suivant :
using Microsoft.AspNetCore.Identity.UI.Services; namespace RazorPagesPizza.Services; public class EmailSender : IEmailSender { public EmailSender() {} public Task SendEmailAsync(string email, string subject, string htmlMessage) { Console.WriteLine(); Console.WriteLine("Email Confirmation Message"); Console.WriteLine("--------------------------"); Console.WriteLine($"TO: {email}"); Console.WriteLine($"SUBJECT: {subject}"); Console.WriteLine($"CONTENTS: {htmlMessage}"); Console.WriteLine(); return Task.CompletedTask; } }
Le code précédent crée une implémentation de IEmailSender qui écrit le contenu du message dans la console. Dans une vraie implémentation,
SendEmailAsync
se connecterait à un service de messagerie externe ou effectuerait une quelqu’autre action pour envoyer un e-mail.Dans Program.cs, ajoutez les lignes mises en surbrillance :
using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using RazorPagesPizza.Areas.Identity.Data; using Microsoft.AspNetCore.Identity.UI.Services; using RazorPagesPizza.Services; var builder = WebApplication.CreateBuilder(args); var connectionString = builder.Configuration.GetConnectionString("RazorPagesPizzaAuthConnection"); builder.Services.AddDbContext<RazorPagesPizzaAuth>(options => options.UseSqlServer(connectionString)); builder.Services.AddDefaultIdentity<RazorPagesPizzaUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<RazorPagesPizzaAuth>(); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddTransient<IEmailSender, EmailSender>(); var app = builder.Build();
Le précédent inscrit
EmailSender
commeIEmailSender
dans le système d’injection de dépendances.
Tester les changements du formulaire d’inscription
C’est tout ! Nous allons tester les changements du formulaire d’inscription et de l’e-mail de confirmation.
Vérifiez que vous avez enregistré toutes vos modifications.
Dans le volet du terminal, générez le projet et exécutez l’application avec
dotnet run
.Dans votre navigateur, accédez à l’application. Sélectionnez Se déconnecter si vous êtes connecté.
Sélectionnez Inscrire et utilisez le formulaire mis à jour pour inscrire un nouvel utilisateur.
Notes
Les contraintes de validation sur les champs Prénom et Nom reflètent les annotations de données sur les propriétés
FirstName
etLastName
deInputModel
.Après l’inscription, vous êtes redirigé vers l’écran Confirmation d’inscription. Dans le volet du terminal, faites défiler vers le haut pour rechercher la sortie de la console qui ressemble à ce qui suit :
Email Confirmation Message -------------------------- TO: jana.heinrich@contoso.com SUBJECT: Confirm your email CONTENTS: Please confirm your account by visiting the following URL: https://localhost:7192/Identity/Account/ConfirmEmail?<query string removed>
Accédez à l’URL avec Ctrl+Clic. L’écran de confirmation s’affiche.
Notes
Si vous utilisez GitHub Codespaces, vous devrez peut-être ajouter
-7192
à la première partie de l’URL transférée. Par exemple :scaling-potato-5gr4j4-7192.preview.app.github.dev
.Sélectionnez Connexion et connectez-vous avec le nouvel utilisateur. L’en-tête de l’application contient maintenant Bonjour [Prénom] [Nom] !.
Dans le volet SQL Server de VS Code, cliquez avec le bouton droit sur la base de données RazorPagesPizza, puis sélectionnez Nouvelle requête. Sous l’onglet qui s’affiche, entrez la requête suivante et appuyez sur Ctrl+Maj+E pour l’exécuter.
SELECT UserName, Email, FirstName, LastName FROM dbo.AspNetUsers
Un onglet avec des résultats similaires à ce qui suit s’affiche :
UserName E-mail FirstName LastName kai.klein@contoso.com kai.klein@contoso.com jana.heinrich@contoso.com jana.heinrich@contoso.com Jana Heinrich Le premier utilisateur s’est inscrit avant d’ajouter
FirstName
etLastName
au schéma. Ainsi, l’enregistrement de la tableAspNetUsers
associé ne contient pas de données dans ces colonnes.
Tester les changements apportés au formulaire de gestion des profils
Vous devez également tester les changements que vous avez apportés au formulaire de gestion des profils.
Dans l’application web, connectez-vous avec le premier utilisateur que vous avez créé.
Sélectionnez le lien Hello, ! pour accéder au formulaire de gestion des profils.
Notes
Le lien ne s’affiche pas correctement parce que la ligne de la table
AspNetUsers
pour cet utilisateur ne contient pas de valeurs pourFirstName
etLastName
.Entrez des valeurs valides pour Prénom et Nom. Sélectionnez Enregistrer.
L’en-tête de l’application est mis à jour avec Bonjour [Prénom] [Nom] !.
Pour arrêter l’application, appuyez sur Ctrl+C dans le volet du terminal de VS Code.
Résumé
Dans cette unité, vous avez personnalisé Identity pour stocker des informations utilisateur personnalisées. Vous avez également personnalisé l’e-mail de confirmation. Dans la prochaine unité, vous allez apprendre à implémenter l’authentification multifacteur dans Identity.