Aggiungere, scaricare ed eliminare dati utente personalizzati per Identity in un progetto ASP.NET Core

Questo articolo illustra come:

  • Aggiungere dati utente personalizzati a un'app Web ASP.NET Core.
  • Contrassegnare il modello di dati utente personalizzato con l'attributo PersonalDataAttribute in modo che sia automaticamente disponibile per il download e l'eliminazione. Rendere i dati in grado di essere scaricati ed eliminati consente di soddisfare i requisiti del GDPR .

L'esempio di progetto viene creato da un'app Web Razor Pages, ma le istruzioni sono simili per un'app Web MVC ASP.NET Core.

Visualizzare o scaricare il codice di esempio (come scaricare)

Prerequisites

.NET 10.0 SDK

Guida introduttiva: Aggiungere due proprietà personalizzate

Nel caso comune dell'aggiunta di due proprietà personalizzate come FirstName e LastName:

  1. Creare un nuovo progetto di ASP.NET Core con l'autenticazione dei singoli account
  2. Scaffold Identity per sovrascrivere le pagine Register e Manage/Index
  3. Aggiungere proprietà personalizzate, FirstName ad esempio e LastName alla classe utente, con l'attributo [PersonalData] .
  4. Aggiornare le pagine Registra e Gestisci/Indice per includere i campi di input per le proprietà.
  5. Crea e applica una migrazione.
  6. Registrazione ai test e gestione del profilo.

Le sezioni seguenti forniscono istruzioni dettagliate per questo processo.

Creare un'app Web

È possibile creare un'app Razor Web Pages o MVC. Entrambi usano la stessa IdentityRazor interfaccia utente di Pages.

Razor Pagine

  • Dal menu Visual Studio File selezionare Nuovo>Project/Soluzione....
  • Seleziona App Web ASP.NET Core (RazorPagine)>Avanti
  • Assegna al progetto il nome WebApp1 se vuoi che corrisponda al namespace del codice di esempio scaricabile. Seleziona Avanti.
  • Seleziona Tipo di autenticazione>Account individuali>Crea
  • Compilare ed eseguire il progetto.

MVC

  • Dal menu Visual Studio File selezionare Nuovo>Project/Soluzione....
  • Seleziona app Web ASP.NET Core (Model-View-Controller)>Avanti
  • Assegna al progetto il nome WebApp1 se vuoi che corrisponda allo spazio dei nomi del codice di esempio scaricato . Seleziona Avanti.
  • Seleziona Tipo di autenticazione>Account individuali>Crea
  • Compilare ed eseguire il progetto.

Note

Identity L'interfaccia utente viene implementata come libreria di Razor classi. Quando si crea un progetto MVC con autenticazione, le Identity pagine vengono servite dalla Razor libreria di classi, il che significa che il progetto MVC usa Razor Pages per Identity anche se il resto del progetto usa controller e visualizzazioni MVC.

Eseguire lo Identity scaffolder

  • Da Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Aggiungi>Nuovo elemento con scaffolding.
  • Nel riquadro sinistro della finestra di dialogo Aggiungi scaffolding, selezionare Identity>Aggiungi.
  • Nella finestra di dialogo Aggiungi Identity sono disponibili le opzioni seguenti:
    • Selezionare il file ~/Pages/Shared/_Layout.cshtml di layout esistente per Razor Pages o ~/Views/Shared/_Layout.cshtml per MVC
    • Selezionare i file seguenti per eseguire l'override:
      • Account/Register
      • Account/Manage/Index
    • Selezionare il + pulsante per creare una nuova classe contesto dati. Accettare il tipo (WebApp1.Models.WebApp1Context se il progetto è denominato WebApp1).
    • Selezionare il + pulsante per creare una nuova classe User. Accettare il tipo (WebApp1User se il progetto è denominato WebApp1) >Aggiungi.
  • Seleziona Aggiungi.

Seguire le istruzioni riportate in Migrazioni, UseAuthentication e layout per eseguire la procedura seguente:

  • Creare una migrazione e aggiornare il database.
  • Aggiungi UseAuthentication a Program.cs
  • Aggiungere <partial name="_LoginPartial" /> al file di layout.
  • Testare l'app:
    • Registrare un utente
    • Selezionare il nuovo nome utente accanto al collegamento Disconnessione . Potrebbe essere necessario espandere la finestra o selezionare l'icona della barra di spostamento per visualizzare il nome utente e altri collegamenti.
    • Selezionare la scheda Dati personali .
    • Selezionare il pulsante Scarica ed esaminare il PersonalData.json file.
    • Testare il pulsante Elimina , che elimina l'utente connesso.

Aggiungere dati utente personalizzati al Identity database

Aggiornare la IdentityUser classe derivata con proprietà personalizzate. Se hai nominato il progetto WebApp1, il file è denominato Areas/Identity/Data/WebApp1User.cs. Aggiornare il file con il codice seguente:

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; }
}

Le proprietà con l'attributo PersonalData sono:

  • Eliminato quando la Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor pagina chiama UserManager.Delete.
  • Inclusi nei dati scaricati dalla Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor pagina.

Aggiornare la Account/Manage/Index.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs con il seguente codice evidenziato:

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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Manage/Index.cshtml con il seguente markup evidenziato:

@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" />
}

Aggiornare la Account/Register.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs con il seguente codice evidenziato:

    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;
        }
    }
}

Aggiorna il Areas/Identity/Pages/Account/Register.cshtml con il seguente markup evidenziato:

@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" />
}

Costruisci il progetto.

Aggiornare il layout

Vedere Modifiche al layout per le istruzioni su come aggiungere i collegamenti di accesso e uscita a ogni pagina.

Aggiungere una migrazione per i dati utente personalizzati

Nella Console di Gestione Pacchetti di Visual Studio:

Add-Migration CustomUserData
Update-Database

Testare la creazione, visualizzazione, scaricamento, eliminazione di dati utente personalizzati

Eseguire il test dell'app:

  • Registrare un nuovo utente.
  • Visualizzare i dati utente personalizzati nella /Identity/Account/Manage pagina.
  • Scaricare e visualizzare i dati personali degli utenti dalla /Identity/Account/Manage/PersonalData pagina.

Troubleshooting

Se riscontri problemi:

Sintomo Causa probabile Soluzione
I campi personalizzati non vengono visualizzati nel modulo di registrazione Si è dimenticato di aggiornare Register.cshtml Assicurati di aver generato la struttura e apportato le modifiche Areas/Identity/Pages/Account/Register.cshtml
Dati personalizzati non salvati Si è dimenticato di aggiungere la migrazione o aggiornare il database Eseguire dotnet ef migrations add CustomUserData poi dotnet ef database update
Campi personalizzati non presenti nei dati scaricati Attributo mancante [PersonalData] Aggiungere l'attributo [PersonalData] alle proprietà della classe utente
Errori di compilazione dopo lo scaffolding Mancata corrispondenza della versione del pacchetto Verificare che tutti i Identity pacchetti corrispondano alla versione del framework di destinazione

.NET 9 SDK

Note

Gli esempi di codice e le istruzioni per .NET 9 fanno riferimento alla cartella di esempio 10.x. Il codice è identico tra .NET 9 e .NET 10 per questo scenario. Solo il framework di destinazione e le versioni del pacchetto differiscono nel file di progetto.

Guida introduttiva: Aggiungere due proprietà personalizzate

Per il caso comune dell'aggiunta di due proprietà personalizzate come FirstName e LastName:

  1. Creare un nuovo progetto di ASP.NET Core con l'autenticazione dei singoli account
  2. Eseguire lo scaffolding Identity per sovrascrivere le pagine Register e Manage/Index
  3. Aggiungere proprietà personalizzate, FirstName ad esempio e LastName alla classe utente, con l'attributo [PersonalData] .
  4. Aggiornare le pagine Registra e Gestisci/Indice per includere i campi di input per le proprietà
  5. Creare e applicare una migrazione
  6. Testare la registrazione e la gestione dei profili

Le sezioni seguenti forniscono istruzioni dettagliate per questo processo.

Creare un'app Web

È possibile creare un'app Razor Web Pages o MVC. Entrambi usano la stessa IdentityRazor interfaccia utente di Pages.

Razor Pagine

  • Dal menu Visual Studio File selezionare New>Project. Assegnare al progetto il nome WebApp1 se si vuole che corrisponda al namespace del codice di esempio scaricato.
  • Seleziona ASP.NET Core Web App>Avanti
  • Seleziona Tipo di autenticazione>Account individuali>Crea
  • Compilare ed eseguire il progetto.

MVC

  • Dal menu Visual Studio File selezionare New>Project. Assegnare al progetto il nome WebApp1 se si vuole che corrisponda al namespace del codice di esempio scaricabile.
  • Selezionare App Web ASP.NET Core (Model-View-Controller)>Avanti
  • Seleziona Tipo di autenticazione>Account individuali>Crea
  • Compilare ed eseguire il progetto.

Note

Identity L'interfaccia utente viene implementata come libreria di Razor classi. Quando si crea un progetto MVC con autenticazione, le Identity pagine vengono servite dalla Razor libreria di classi, il che significa che il progetto MVC usa Razor Pages per Identity anche se il resto del progetto usa controller e visualizzazioni MVC.

Eseguire lo Identity scaffolder

  • Da Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Aggiungi>Nuovo elemento con scaffolding.
  • Nel riquadro sinistro della finestra di dialogo Aggiungi scaffolding, selezionare Identity>Aggiungi.
  • Nella finestra di dialogo Aggiungi Identity sono disponibili le opzioni seguenti:
    • Selezionare il file ~/Pages/Shared/_Layout.cshtml di layout esistente per Razor Pages o ~/Views/Shared/_Layout.cshtml per MVC
    • Selezionare i file seguenti per eseguire l'override:
      • Account/Register
      • Account/Manage/Index
    • Selezionare il + pulsante per creare una nuova classe contesto dati. Accettare il tipo (WebApp1.Models.WebApp1Context se il progetto è denominato WebApp1).
    • Selezionare il + pulsante per creare una nuova classe User. Accettare il tipo (WebApp1User se il progetto è denominato WebApp1) >Aggiungi.
  • Seleziona Aggiungi.

Seguire le istruzioni riportate in Migrazioni, UseAuthentication e layout per eseguire la procedura seguente:

  • Creare una migrazione e aggiornare il database.
  • Aggiungi UseAuthentication a Program.cs
  • Aggiungere <partial name="_LoginPartial" /> al file di layout.
  • Testare l'app:
    • Registrare un utente
    • Selezionare il nuovo nome utente accanto al collegamento Disconnessione . Potrebbe essere necessario espandere la finestra o selezionare l'icona della barra di spostamento per visualizzare il nome utente e altri collegamenti.
    • Selezionare la scheda Dati personali .
    • Selezionare il pulsante Scarica ed esaminare il PersonalData.json file.
    • Testare il pulsante Elimina , che elimina l'utente connesso.

Aggiungere dati utente personalizzati al Identity database

Aggiornare la IdentityUser classe derivata con proprietà personalizzate. Se hai nominato il progetto WebApp1, il file è denominato Areas/Identity/Data/WebApp1User.cs. Aggiornare il file con il codice seguente:

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; }
}

Le proprietà con l'attributo PersonalData sono:

  • Eliminato quando la Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor pagina chiama UserManager.Delete.
  • Inclusi nei dati scaricati dalla Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor pagina.

Aggiornare la Account/Manage/Index.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs con il seguente codice evidenziato:

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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Manage/Index.cshtml con il seguente markup evidenziato:

@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" />
}

Aggiornare la Account/Register.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs con il seguente codice evidenziato:

    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;
        }
    }
}

Aggiorna il Areas/Identity/Pages/Account/Register.cshtml con il seguente markup evidenziato:

@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" />
}

Costruisci il progetto.

Aggiornare il layout

Vedere Modifiche al layout per le istruzioni su come aggiungere i collegamenti di accesso e uscita a ogni pagina.

Aggiungere una migrazione per i dati utente personalizzati

Nella Console di Gestione Pacchetti di Visual Studio:

Add-Migration CustomUserData
Update-Database

Testare la creazione, visualizzazione, scaricamento, eliminazione di dati utente personalizzati

Eseguire il test dell'app:

  • Registrare un nuovo utente.
  • Visualizzare i dati utente personalizzati nella /Identity/Account/Manage pagina.
  • Scaricare e visualizzare i dati personali degli utenti dalla /Identity/Account/Manage/PersonalData pagina.

Troubleshooting

Se riscontri problemi:

Sintomo Causa probabile Soluzione
I campi personalizzati non vengono visualizzati nel modulo di registrazione Si è dimenticato di aggiornare Register.cshtml Assicurati di aver generato la struttura e apportato le modifiche Areas/Identity/Pages/Account/Register.cshtml
Dati personalizzati non salvati Si è dimenticato di aggiungere la migrazione o aggiornare il database Eseguire dotnet ef migrations add CustomUserData quindi dotnet ef database update
Campi personalizzati non presenti nei dati scaricati Attributo mancante [PersonalData] Aggiungi l'attributo [PersonalData] alle proprietà della tua classe utente
Errori di compilazione dopo lo scaffolding Mancata corrispondenza della versione del pacchetto Verificare che tutti i Identity pacchetti corrispondano alla versione del framework di destinazione

.NET 8 SDK

Note

Gli esempi di codice e le istruzioni per .NET 8 fanno riferimento alla cartella di esempio 10.x. Il codice è identico tra .NET 8 e .NET 10 per questo scenario. Solo il framework di destinazione e le versioni del pacchetto differiscono nel file di progetto.

Guida introduttiva: Aggiungere due proprietà personalizzate

Per uno scenario semplice in cui si vogliono aggiungere solo un paio di proprietà personalizzate, ad esempio FirstName e LastName all'utente Identity :

  1. Creare un nuovo progetto di ASP.NET Core con l'autenticazione dei singoli account
  2. Generare lo scaffolding Identity per sovrascrivere le pagine Register e Manage/Index
  3. Aggiungere le proprietà personalizzate alla classe utente (ad esempio, FirstName, LastName) con l'attributo [PersonalData]
  4. Aggiornare le pagine Registra e Gestisci/Indice per includere i campi di input per le proprietà
  5. Creare e applicare una migrazione
  6. Testare la registrazione e la gestione dei profili

Le sezioni seguenti forniscono istruzioni dettagliate per questo processo.

Creare un'app Web

È possibile creare un'app Razor Web Pages o MVC. Entrambi usano la stessa IdentityRazor interfaccia utente di Pages.

Razor Pagine

  • Dal menu Visual Studio File selezionare New>Project. Assegnare al progetto il nome WebApp1 se si vuole che corrisponda al namespace del codice di esempio scaricabile.
  • Seleziona App Web ASP.NET Core>Avanti
  • Seleziona Tipo di autenticazione>Account individuali>Crea
  • Compilare ed eseguire il progetto.

MVC

  • Dal menu Visual Studio File selezionare New>Project. Assegna al progetto il nome WebApp1 se vuoi che corrisponda allo spazio dei nomi del codice di esempio scaricato .
  • Seleziona app Web ASP.NET Core (Model-View-Controller)>Avanti
  • Seleziona Tipo di autenticazione>Account individuali>Crea
  • Compilare ed eseguire il progetto.

Note

Identity L'interfaccia utente viene implementata come libreria di Razor classi. Quando si crea un progetto MVC con autenticazione, le Identity pagine vengono servite dalla Razor libreria di classi, il che significa che il progetto MVC usa Razor Pages per Identity anche se il resto del progetto usa controller e visualizzazioni MVC.

Eseguire lo Identity scaffolder

  • Da Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Aggiungi>Nuovo elemento con scaffolding.
  • Nel riquadro sinistro della finestra di dialogo Aggiungi scaffolding, selezionare Identity>Aggiungi.
  • Nella finestra di dialogo Aggiungi Identity sono disponibili le opzioni seguenti:
    • Selezionare il file ~/Pages/Shared/_Layout.cshtml di layout esistente per Razor Pages o ~/Views/Shared/_Layout.cshtml per MVC
    • Selezionare i file seguenti per eseguire l'override:
      • Account/Register
      • Account/Manage/Index
    • Selezionare il + pulsante per creare una nuova classe contesto dati. Accettare il tipo (WebApp1.Models.WebApp1Context se il progetto è denominato WebApp1).
    • Selezionare il + pulsante per creare una nuova classe User. Accettare il tipo (WebApp1User se il progetto è denominato WebApp1) >Aggiungi.
  • Seleziona Aggiungi.

Seguire le istruzioni riportate in Migrazioni, UseAuthentication e layout per eseguire la procedura seguente:

  • Creare una migrazione e aggiornare il database.
  • Aggiungi UseAuthentication a Program.cs
  • Aggiungere <partial name="_LoginPartial" /> al file di layout.
  • Testare l'app:
    • Registrare un utente
    • Selezionare il nuovo nome utente accanto al collegamento Disconnessione . Potrebbe essere necessario espandere la finestra o selezionare l'icona della barra di spostamento per visualizzare il nome utente e altri collegamenti.
    • Selezionare la scheda Dati personali .
    • Selezionare il pulsante Scarica ed esaminare il PersonalData.json file.
    • Testare il pulsante Elimina , che elimina l'utente connesso.

Aggiungere dati utente personalizzati al Identity database

Aggiornare la IdentityUser classe derivata con proprietà personalizzate. Se hai nominato il progetto WebApp1, il file è denominato Areas/Identity/Data/WebApp1User.cs. Aggiornare il file con il codice seguente:

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; }
}

Le proprietà con l'attributo PersonalData sono:

  • Eliminato quando la Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor pagina chiama UserManager.Delete.
  • Inclusi nei dati scaricati dalla Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor pagina.

Aggiornare la Account/Manage/Index.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs con il seguente codice evidenziato:

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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Manage/Index.cshtml con il seguente markup evidenziato:

@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" />
}

Aggiornare la Account/Register.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs con il seguente codice evidenziato:

    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;
        }
    }
}

Aggiorna il Areas/Identity/Pages/Account/Register.cshtml con il seguente markup evidenziato:

@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" />
}

Costruisci il progetto.

Aggiornare il layout

Vedere Modifiche al layout per le istruzioni su come aggiungere i collegamenti di accesso e uscita a ogni pagina.

Aggiungere una migrazione per i dati utente personalizzati

Nella Console di Gestione Pacchetti di Visual Studio:

Add-Migration CustomUserData
Update-Database

Testare la creazione, visualizzazione, scaricamento, eliminazione di dati utente personalizzati

Eseguire il test dell'app:

  • Registrare un nuovo utente.
  • Visualizzare i dati utente personalizzati nella /Identity/Account/Manage pagina.
  • Scaricare e visualizzare i dati personali degli utenti dalla /Identity/Account/Manage/PersonalData pagina.

Troubleshooting

Se riscontri problemi:

Sintomo Causa probabile Soluzione
I campi personalizzati non vengono visualizzati nel modulo di registrazione Si è dimenticato di aggiornare Register.cshtml Assicurati di aver generato la struttura e apportato le modifiche Areas/Identity/Pages/Account/Register.cshtml
Dati personalizzati non salvati Si è dimenticato di aggiungere la migrazione o aggiornare il database Eseguire dotnet ef migrations add CustomUserData quindi dotnet ef database update
Campi personalizzati non presenti nei dati scaricati Attributo mancante [PersonalData] Aggiungere l'attributo [PersonalData] alle proprietà nella classe utente
Errori di compilazione dopo lo scaffolding Mancata corrispondenza della versione del pacchetto Verificare che tutti i Identity pacchetti corrispondano alla versione del framework di destinazione

.NET 6 SDK

Creare un'app Razor Web

  • Dal menu Visual Studio File selezionare New>Project. Assegnare al progetto il nome WebApp1 se si desidera che corrisponda allo spazio dei nomi del codice dell'esempio scaricato.
  • Selezionare ASP.NET Core Applicazione Web>OK
  • Selezionare Web Application>OK
  • Compilare ed eseguire il progetto.

Eseguire lo Identity scaffolder

  • Da Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Aggiungi>Nuovo elemento con scaffolding.
  • Nel riquadro sinistro della finestra di dialogo Aggiungi scaffolding, selezionare Identity>Aggiungi.
  • Nella finestra di dialogo Aggiungi Identity sono disponibili le opzioni seguenti:
    • Selezionare il file di layout esistente ~/Pages/Shared/_Layout.cshtml
    • Selezionare i file seguenti per eseguire l'override:
      • Account/Register
      • Account/Manage/Index
    • Selezionare il + pulsante per creare una nuova classe contesto dati. Accettare il tipo (WebApp1.Models.WebApp1Context se il progetto è denominato WebApp1).
    • Selezionare il + pulsante per creare una nuova classe User. Accettare il tipo (WebApp1User se il progetto è denominato WebApp1) >Aggiungi.
  • Seleziona Aggiungi.

Seguire le istruzioni riportate in Migrazioni, UseAuthentication e layout per eseguire la procedura seguente:

  • Creare una migrazione e aggiornare il database.
  • Aggiungi UseAuthentication a Program.cs
  • Aggiungere <partial name="_LoginPartial" /> al file di layout.
  • Testare l'app:
    • Registrare un utente
    • Selezionare il nuovo nome utente accanto al collegamento Disconnessione . Potrebbe essere necessario espandere la finestra o selezionare l'icona della barra di spostamento per visualizzare il nome utente e altri collegamenti.
    • Selezionare la scheda Dati personali .
    • Selezionare il pulsante Scarica ed esaminare il PersonalData.json file.
    • Testare il pulsante Elimina , che elimina l'utente connesso.

Aggiungere dati utente personalizzati al Identity database

Aggiornare la IdentityUser classe derivata con proprietà personalizzate. Se hai nominato il progetto WebApp1, il file è denominato Areas/Identity/Data/WebApp1User.cs. Aggiornare il file con il codice seguente:

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; }
}

Le proprietà con l'attributo PersonalData sono:

  • Eliminato quando la Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor pagina chiama UserManager.Delete.
  • Inclusi nei dati scaricati dalla Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor pagina.

Aggiornare la Account/Manage/Index.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs con il seguente codice evidenziato:

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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Manage/Index.cshtml con il seguente markup evidenziato:

@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" />
}

Aggiornare la Account/Register.cshtml pagina

Aggiorna InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs con il seguente codice evidenziato:

    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;
        }
    }
}

Aggiorna il Areas/Identity/Pages/Account/Register.cshtml con il seguente markup evidenziato:

@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" />
}

Costruisci il progetto.

Aggiornare il layout

Vedere Modifiche al layout per le istruzioni su come aggiungere i collegamenti di accesso e uscita a ogni pagina.

Aggiungere una migrazione per i dati utente personalizzati

Nella Console di Gestione Pacchetti di Visual Studio:

Add-Migration CustomUserData
Update-Database

Testare la creazione, visualizzazione, scaricamento, eliminazione di dati utente personalizzati

Eseguire il test dell'app:

  • Registrare un nuovo utente.
  • Visualizzare i dati utente personalizzati nella /Identity/Account/Manage pagina.
  • Scaricare e visualizzare i dati personali degli utenti dalla /Identity/Account/Manage/PersonalData pagina.

.NET Core 3.0 SDK

Creare un'app Razor Web

  • Dal menu Visual Studio File selezionare New>Project. Assegnare al progetto il nome WebApp1 se si desidera che corrisponda allo spazio dei nomi del codice dell'esempio scaricato.
  • Selezionare ASP.NET Core Applicazione Web>OK
  • Selezionare Web Application>OK
  • Compilare ed eseguire il progetto.

Eseguire lo Identity scaffolder

  • Da Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Aggiungi>Nuovo elemento con scaffolding.
  • Nel riquadro sinistro della finestra di dialogo Aggiungi scaffolding, selezionare Identity>Aggiungi.
  • Nella finestra di dialogo Aggiungi Identity sono disponibili le opzioni seguenti:
    • Selezionare il file di layout esistente ~/Pages/Shared/_Layout.cshtml
    • Selezionare i file seguenti per eseguire l'override:
      • Account/Register
      • Account/Manage/Index
    • Selezionare il + pulsante per creare una nuova classe contesto dati. Accettare il tipo (WebApp1.Models.WebApp1Context se il progetto è denominato WebApp1).
    • Selezionare il + pulsante per creare una nuova classe User. Accettare il tipo (WebApp1User se il progetto è denominato WebApp1) >Aggiungi.
  • Seleziona Aggiungi.

Seguire le istruzioni riportate in Migrazioni, UseAuthentication e layout per eseguire la procedura seguente:

  • Creare una migrazione e aggiornare il database.
  • Aggiungi UseAuthentication a Startup.Configure.
  • Aggiungere <partial name="_LoginPartial" /> al file di layout.
  • Testare l'app:
    • Registrare un utente
    • Selezionare il nuovo nome utente accanto al collegamento Disconnessione . Potrebbe essere necessario espandere la finestra o selezionare l'icona della barra di spostamento per visualizzare il nome utente e altri collegamenti.
    • Selezionare la scheda Dati personali .
    • Selezionare il pulsante Scarica ed esaminare il PersonalData.json file.
    • Testare il pulsante Elimina , che elimina l'utente connesso.

Aggiungere dati utente personalizzati al Identity database

Aggiornare la IdentityUser classe derivata con proprietà personalizzate. Se hai nominato il progetto WebApp1, il file è denominato Areas/Identity/Data/WebApp1User.cs. Aggiornare il file con il codice seguente:

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; }
    }
}

Le proprietà con l'attributo PersonalData sono:

  • Eliminato quando la Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor pagina chiama UserManager.Delete.
  • Inclusi nei dati scaricati dalla Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor pagina.

Aggiornare la pagina Account/Manage/Index.cshtml

Aggiorna InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs con il seguente codice evidenziato:

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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Manage/Index.cshtml con il seguente markup evidenziato:

@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" />
}

Aggiornare la pagina Account/Register.cshtml

Aggiorna InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs con il seguente codice evidenziato:

[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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Register.cshtml con il seguente markup evidenziato:

@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" />
}

Costruisci il progetto.

Aggiungere una migrazione per i dati utente personalizzati

Nella Console di Gestione Pacchetti di Visual Studio:

Add-Migration CustomUserData
Update-Database

Testare la creazione, visualizzazione, scaricamento, eliminazione di dati utente personalizzati

Eseguire il test dell'app:

  • Registrare un nuovo utente.
  • Visualizzare i dati utente personalizzati nella /Identity/Account/Manage pagina.
  • Scaricare e visualizzare i dati personali degli utenti dalla /Identity/Account/Manage/PersonalData pagina.

Aggiungere attestazioni a Identity usando IUserClaimsPrincipalFactory<ApplicationUser>

Note

Questa sezione non è un'estensione dell'esercitazione precedente. Per applicare i passaggi seguenti all'app compilata usando l'esercitazione, vedere questo problema di GitHub.

È possibile aggiungere attestazioni aggiuntive a ASP.NET Core Identity usando l'interfaccia IUserClaimsPrincipalFactory<T>. Questa classe può essere aggiunta all'app nel Startup.ConfigureServices metodo . Aggiungere l'implementazione personalizzata della classe come indicato di seguito:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, 
        AdditionalUserClaimsPrincipalFactory>();

Il codice demo usa la ApplicationUser classe . Questa classe aggiunge una proprietà IsAdmin utilizzata per fornire un'ulteriore attestazione.

public class ApplicationUser : IdentityUser
{
    public bool IsAdmin { get; set; }
}

AdditionalUserClaimsPrincipalFactory Implementa l'interfaccia UserClaimsPrincipalFactory . Viene aggiunta una nuova attestazione di ruolo a 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;
    }
}

La dichiarazione aggiuntiva può quindi essere utilizzata nell'app. In una pagina Razor, è possibile usare l'istanza IAuthorizationService per accedere al valore del claim.

@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 o versione successiva dell'SDK

Creare un'app Razor Web

  • Dal menu Visual Studio File selezionare New>Project. Assegnare al progetto il nome WebApp1 se si desidera che corrisponda allo spazio dei nomi del codice dell'esempio scaricato.
  • Selezionare ASP.NET Core Applicazione Web>OK
  • Selezionare ASP.NET Core 2.2 nell'elenco a discesa
  • Selezionare Web Application>OK
  • Compilare ed eseguire il progetto.

Eseguire lo Identity scaffolder

  • Da Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Aggiungi>Nuovo elemento con scaffolding.
  • Nel riquadro sinistro della finestra di dialogo Aggiungi scaffolding, selezionare Identity>Aggiungi.
  • Nella finestra di dialogo Aggiungi Identity sono disponibili le opzioni seguenti:
    • Selezionare il file di layout esistente ~/Pages/Shared/_Layout.cshtml
    • Selezionare i file seguenti per eseguire l'override:
      • Account/Register
      • Account/Manage/Index
    • Selezionare il + pulsante per creare una nuova classe contesto dati. Accettare il tipo (WebApp1.Models.WebApp1Context se il progetto è denominato WebApp1).
    • Selezionare il + pulsante per creare una nuova classe User. Accettare il tipo (WebApp1User se il progetto è denominato WebApp1) >Aggiungi.
  • Seleziona Aggiungi.

Seguire le istruzioni riportate in Migrazioni, UseAuthentication e layout per eseguire la procedura seguente:

  • Creare una migrazione e aggiornare il database.
  • Aggiungi UseAuthentication a Startup.Configure.
  • Aggiungere <partial name="_LoginPartial" /> al file di layout.
  • Testare l'app:
    • Registrare un utente
    • Selezionare il nuovo nome utente accanto al collegamento Disconnessione . Potrebbe essere necessario espandere la finestra o selezionare l'icona della barra di spostamento per visualizzare il nome utente e altri collegamenti.
    • Selezionare la scheda Dati personali .
    • Selezionare il pulsante Scarica ed esaminare il PersonalData.json file.
    • Testare il pulsante Elimina , che elimina l'utente connesso.

Aggiungere dati utente personalizzati al Identity database

Aggiornare la IdentityUser classe derivata con proprietà personalizzate. Se hai nominato il progetto WebApp1, il file è denominato Areas/Identity/Data/WebApp1User.cs. Aggiornare il file con il codice seguente:

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; }
    }
}

Le proprietà con l'attributo PersonalData sono:

  • Eliminato quando la Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor pagina chiama UserManager.Delete.
  • Inclusi nei dati scaricati dalla Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor pagina.

Aggiornare la pagina Account/Manage/Index.cshtml

Aggiorna InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs con il seguente codice evidenziato:

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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Manage/Index.cshtml con il seguente markup evidenziato:

@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" />
}

Aggiornare la pagina Account/Register.cshtml

Aggiorna InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs con il seguente codice evidenziato:

[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();
    }
}

Aggiorna il Areas/Identity/Pages/Account/Register.cshtml con il seguente markup evidenziato:

@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" />
}

Costruisci il progetto.

Aggiungere una migrazione per i dati utente personalizzati

Le proprietà personalizzate aggiunte alla WebApp1UserIdentity classe utente devono essere riflesse nel database. È ora necessaria una migrazione per generare il codice SQL necessario per aggiungere le nuove colonne e quindi il database deve essere aggiornato per applicare tali modifiche.

Lo scaffolder Identity ha creato un nuovo DbContext (WebApp1Context) accanto al ApplicationDbContext originale dal modello del progetto. È necessario specificare il nuovo WebApp1ContextIdentityDbContext da usare durante l'esecuzione dei comandi di migrazione.

Nella Console di Gestione Pacchetti di Visual Studio:

Add-Migration CustomUserData -Context WebApp1Context
Update-Database -Context WebApp1Context

Testare la creazione, visualizzazione, scaricamento, eliminazione di dati utente personalizzati

Eseguire il test dell'app:

  • Registrare un nuovo utente.
  • Visualizzare i dati utente personalizzati nella /Identity/Account/Manage pagina.
  • Scaricare e visualizzare i dati personali degli utenti dalla /Identity/Account/Manage/PersonalData pagina.