Partager via


Partie Inscription et validation

par Jon Galloway

Le Magasin de musique MVC est une application de tutoriel qui présente et explique pas à pas comment utiliser ASP.NET MVC et Visual Studio pour le développement web.

Le Magasin de musique MVC est un exemple d’implémentation de magasin léger qui vend des albums de musique en ligne et implémente l’administration de site de base, la connexion utilisateur et les fonctionnalités de panier d’achat.

Cette série de tutoriels détaille toutes les étapes effectuées pour générer l’exemple d’application ASP.NET magasin de musique MVC. La partie 9 traite de l’inscription et de l’extraction.

Dans cette section, nous allons créer un CheckoutController qui collectera l’adresse et les informations de paiement du client. Nous exigerons que les utilisateurs s’inscrivent auprès de notre site avant d’effectuer le contrôle. Ce contrôleur aura donc besoin d’une autorisation.

Les utilisateurs accèdent au processus de paiement à partir de leur panier en cliquant sur le bouton « Caisse ».

Capture d’écran de la fenêtre Du Magasin de musique montrant l’affichage de la commande avec le bouton Caisse mis en surbrillance par une flèche rouge.

Si l’utilisateur n’est pas connecté, il est invité à le faire.

Capture d’écran de la fenêtre Du Magasin de musique montrant l’affichage de l’ouverture de session avec les champs Nom d’utilisateur et Mot de passe.

Une fois la connexion réussie, la vue Adresse et Paiement s’affiche à l’utilisateur.

Capture d’écran de la fenêtre Magasin de musique montrant l’adresse et l’affichage de paiement avec des champs permettant de collecter l’adresse d’expédition et les informations de paiement.

Une fois qu’ils ont rempli le formulaire et envoyé la commande, l’écran de confirmation de commande s’affiche.

Capture d’écran de la fenêtre Du Magasin de musique montrant la vue d’extraction complète qui informe l’utilisateur que la commande est terminée.

Si vous tentez d’afficher une commande inexistante ou une commande qui n’appartient pas à vous, la vue Erreur s’affiche.

Capture d’écran de la fenêtre Du Magasin de musique montrant l’affichage d’erreur lorsque l’utilisateur tente d’afficher la commande d’une autre personne ou une commande fictive.

Migration du panier d’achat

Bien que le processus d’achat soit anonyme, lorsque l’utilisateur clique sur le bouton Passer en caisse, il doit s’inscrire et se connecter. Les utilisateurs s’attendent à ce que nous conservions leurs informations de panier d’achat entre les visites. Nous devrons donc associer les informations de panier à un utilisateur lorsqu’ils terminent l’inscription ou la connexion.

C’est en fait très simple à faire, car notre classe ShoppingCart a déjà une méthode qui associera tous les éléments du panier actuel à un nom d’utilisateur. Il nous suffit d’appeler cette méthode lorsqu’un utilisateur termine l’inscription ou la connexion.

Ouvrez la classe AccountController que nous avons ajoutée lors de la configuration de l’appartenance et de l’autorisation. Ajoutez une instruction using référençant MvcMusicStore.Models, puis ajoutez la méthode MigrateShoppingCart suivante :

private void MigrateShoppingCart(string UserName)
{
    // Associate shopping cart items with logged-in user
    var cart = ShoppingCart.GetCart(this.HttpContext);
 
    cart.MigrateCart(UserName);
    Session[ShoppingCart.CartSessionKey] = UserName;
}

Ensuite, modifiez l’action de publication LogOn pour appeler MigrateShoppingCart une fois que l’utilisateur a été validé, comme indiqué ci-dessous :

//
// POST: /Account/LogOn
[HttpPost]
 public ActionResult LogOn(LogOnModel model, string returnUrl)
 {
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            MigrateShoppingCart(model.UserName);
                    
            FormsAuthentication.SetAuthCookie(model.UserName,
                model.RememberMe);
            if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1
                && returnUrl.StartsWith("/")
                && !returnUrl.StartsWith("//") &&
                !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
 }

Apportez la même modification à l’action Inscrire la publication, immédiatement après la création du compte d’utilisateur :

//
// POST: /Account/Register
[HttpPost]
 public ActionResult Register(RegisterModel model)
 {
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        MembershipCreateStatus createStatus;
        Membership.CreateUser(model.UserName, model.Password, model.Email, 
               "question", "answer", true, null, out
               createStatus);
 
        if (createStatus == MembershipCreateStatus.Success)
        {
            MigrateShoppingCart(model.UserName);
                    
            FormsAuthentication.SetAuthCookie(model.UserName, false /*
                  createPersistentCookie */);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError("", ErrorCodeToString(createStatus));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
 }

C’est tout : un panier d’achat anonyme est automatiquement transféré vers un compte d’utilisateur en cas d’inscription ou de connexion réussie.

Création du CheckoutController

Cliquez avec le bouton droit sur le dossier Contrôleurs et ajoutez un nouveau contrôleur au projet nommé CheckoutController à l’aide du modèle contrôleur vide.

Capture d’écran de la fenêtre Ajouter un contrôleur avec le champ Nom du contrôleur rempli avec le texte Checkout Controller.

Tout d’abord, ajoutez l’attribut Authorize au-dessus de la déclaration de classe Controller pour exiger que les utilisateurs s’inscrivent avant le paiement :

namespace MvcMusicStore.Controllers
{
    [Authorize]
    public class CheckoutController : Controller

Remarque : Cela est similaire à la modification que nous avons apportée précédemment à StoreManagerController, mais dans ce cas, l’attribut Authorize exigeait que l’utilisateur soit dans un rôle Administrateur. Dans le contrôleur de paiement, nous exigeons que l’utilisateur soit connecté, mais pas qu’il soit administrateur.

Par souci de simplicité, nous ne traiterons pas d’informations de paiement dans ce tutoriel. Au lieu de cela, nous permetons aux utilisateurs de case activée à l’aide d’un code promotionnel. Nous allons stocker ce code promotionnel à l’aide d’une constante nommée PromoCode.

Comme dans storeController, nous allons déclarer un champ pour contenir une instance de la classe MusicStoreEntities, nommée storeDB. Pour utiliser la classe MusicStoreEntities, nous devons ajouter une instruction using pour l’espace de noms MvcMusicStore.Models. Le haut de notre contrôleur Checkout apparaît ci-dessous.

using System;
using System.Linq;
using System.Web.Mvc;
using MvcMusicStore.Models;
 
namespace MvcMusicStore.Controllers
{
    [Authorize]
    public class CheckoutController : Controller
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();
        const string PromoCode = "FREE";

Le CheckoutController aura les actions de contrôleur suivantes :

AddressAndPayment (méthode GET) affiche un formulaire pour permettre à l’utilisateur d’entrer ses informations.

AddressAndPayment (méthode POST) valide l’entrée et traite la commande.

Terminé s’affiche une fois qu’un utilisateur a terminé avec succès le processus d’extraction. Cette vue inclut le numéro de commande de l’utilisateur, comme confirmation.

Tout d’abord, renommons l’action Indexer le contrôleur (qui a été générée lors de la création du contrôleur) en AddressAndPayment. Cette action du contrôleur affiche simplement le formulaire d’extraction, de sorte qu’elle ne nécessite aucune information de modèle.

//
// GET: /Checkout/AddressAndPayment
public ActionResult AddressAndPayment()
{
    return View();
}

Notre méthode ADDRESSAndPayment POST suit le même modèle que celui que nous avons utilisé dans le StoreManagerController : elle essaie d’accepter l’envoi du formulaire et de terminer la commande et réinitie le formulaire en cas d’échec.

Une fois que l’entrée de formulaire répond à nos exigences de validation pour une commande, nous allons case activée directement la valeur du formulaire PromoCode. En supposant que tout est correct, nous allons enregistrer les informations mises à jour avec la commande, indiquer à l’objet ShoppingCart de terminer le processus de commande et rediriger vers l’action Terminer.

//
// POST: /Checkout/AddressAndPayment
[HttpPost]
public ActionResult AddressAndPayment(FormCollection values)
{
    var order = new Order();
    TryUpdateModel(order);
 
    try
    {
        if (string.Equals(values["PromoCode"], PromoCode,
            StringComparison.OrdinalIgnoreCase) == false)
        {
            return View(order);
        }
        else
        {
            order.Username = User.Identity.Name;
            order.OrderDate = DateTime.Now;
 
            //Save Order
            storeDB.Orders.Add(order);
            storeDB.SaveChanges();
            //Process the order
            var cart = ShoppingCart.GetCart(this.HttpContext);
            cart.CreateOrder(order);
 
            return RedirectToAction("Complete",
                new { id = order.OrderId });
        }
    }
    catch
    {
        //Invalid - redisplay with errors
        return View(order);
    }
}

Une fois le processus d’extraction terminé, les utilisateurs sont redirigés vers l’action Terminer le contrôleur. Cette action effectue une case activée simple pour vérifier que la commande appartient bien à l’utilisateur connecté avant d’afficher le numéro de commande en tant que confirmation.

//
// GET: /Checkout/Complete
public ActionResult Complete(int id)
{
    // Validate customer owns this order
    bool isValid = storeDB.Orders.Any(
        o => o.OrderId == id &&
        o.Username == User.Identity.Name);
 
    if (isValid)
    {
        return View(id);
    }
    else
    {
        return View("Error");
    }
}

Remarque : La vue Erreur a été créée automatiquement pour nous dans le dossier /Views/Shared lorsque nous avons démarré le projet.

Le code CheckoutController complet est le suivant :

using System;
using System.Linq;
using System.Web.Mvc;
using MvcMusicStore.Models;
 
namespace MvcMusicStore.Controllers
{
    [Authorize]
    public class CheckoutController : Controller
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();
        const string PromoCode = "FREE";
        //
        // GET: /Checkout/AddressAndPayment
        public ActionResult AddressAndPayment()
        {
            return View();
        }
        //
        // POST: /Checkout/AddressAndPayment
        [HttpPost]
        public ActionResult AddressAndPayment(FormCollection values)
        {
            var order = new Order();
            TryUpdateModel(order);
 
            try
            {
                if (string.Equals(values["PromoCode"], PromoCode,
                    StringComparison.OrdinalIgnoreCase) == false)
                {
                    return View(order);
                }
                else
                {
                    order.Username = User.Identity.Name;
                    order.OrderDate = DateTime.Now;
 
                    //Save Order
                    storeDB.Orders.Add(order);
                    storeDB.SaveChanges();
                    //Process the order
                    var cart = ShoppingCart.GetCart(this.HttpContext);
                    cart.CreateOrder(order);
 
                    return RedirectToAction("Complete",
                        new { id = order.OrderId });
                }
            }
            catch
            {
                //Invalid - redisplay with errors
                return View(order);
            }
        }
        //
        // GET: /Checkout/Complete
        public ActionResult Complete(int id)
        {
            // Validate customer owns this order
            bool isValid = storeDB.Orders.Any(
                o => o.OrderId == id &&
                o.Username == User.Identity.Name);
 
            if (isValid)
            {
                return View(id);
            }
            else
            {
                return View("Error");
            }
        }
    }
}

Ajout de la vue AddressAndPayment

Maintenant, nous allons créer la vue AddressAndPayment. Cliquez avec le bouton droit sur l’une des actions du contrôleur AddressAndPayment et ajoutez une vue nommée AddressAndPayment qui est fortement tapée en tant que Commande et utilise le modèle Modifier, comme illustré ci-dessous.

Capture d’écran de la fenêtre Ajouter une vue avec le champ Nom de l’affichage, la case à cocher Créer une vue et les listes déroulantes Classe de modèle et Structure de modèle mises en évidence en rouge.

Cette vue utilise deux des techniques que nous avons examinées lors de la création de la vue StoreManagerEdit :

  • Nous allons utiliser Html.EditorForModel() pour afficher les champs de formulaire pour le modèle Order
  • Nous allons tirer parti des règles de validation à l’aide d’une classe Order avec des attributs de validation

Nous allons commencer par mettre à jour le code du formulaire pour utiliser Html.EditorForModel(), suivi d’une zone de texte supplémentaire pour le code promotionnel. Le code complet de la vue AddressAndPayment est illustré ci-dessous.

@model MvcMusicStore.Models.Order
@{
    ViewBag.Title = "Address And Payment";
}
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"></script>
@using (Html.BeginForm()) {
    
    <h2>Address And Payment</h2>
    <fieldset>
        <legend>Shipping Information</legend>
        @Html.EditorForModel()
    </fieldset>
    <fieldset>
        <legend>Payment</legend>
        <p>We're running a promotion: all music is free 
            with the promo code: "FREE"</p>
        <div class="editor-label">
            @Html.Label("Promo Code")
        </div>
        <div class="editor-field">
            @Html.TextBox("PromoCode")
        </div>
    </fieldset>
    
    <input type="submit" value="Submit Order" />
}

Définition de règles de validation pour la commande

Maintenant que notre vue est configurée, nous allons configurer les règles de validation pour notre modèle de commande comme nous l’avons fait précédemment pour le modèle Album. Cliquez avec le bouton droit sur le dossier Modèles et ajoutez une classe nommée Order. En plus des attributs de validation que nous avons utilisés précédemment pour l’album, nous utiliserons également une expression régulière pour valider l’adresse e-mail de l’utilisateur.

using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
 
namespace MvcMusicStore.Models
{
    [Bind(Exclude = "OrderId")]
    public partial class Order
    {
        [ScaffoldColumn(false)]
        public int OrderId { get; set; }
        [ScaffoldColumn(false)]
        public System.DateTime OrderDate { get; set; }
        [ScaffoldColumn(false)]
        public string Username { get; set; }
        [Required(ErrorMessage = "First Name is required")]
        [DisplayName("First Name")]
        [StringLength(160)]
        public string FirstName { get; set; }
        [Required(ErrorMessage = "Last Name is required")]
        [DisplayName("Last Name")]
        [StringLength(160)]
        public string LastName { get; set; }
        [Required(ErrorMessage = "Address is required")]
        [StringLength(70)]
        public string Address { get; set; }
        [Required(ErrorMessage = "City is required")]
        [StringLength(40)]
        public string City { get; set; }
        [Required(ErrorMessage = "State is required")]
        [StringLength(40)]
        public string State { get; set; }
        [Required(ErrorMessage = "Postal Code is required")]
        [DisplayName("Postal Code")]
        [StringLength(10)]
        public string PostalCode { get; set; }
        [Required(ErrorMessage = "Country is required")]
        [StringLength(40)]
        public string Country { get; set; }
        [Required(ErrorMessage = "Phone is required")]
        [StringLength(24)]
        public string Phone { get; set; }
        [Required(ErrorMessage = "Email Address is required")]
        [DisplayName("Email Address")]
       
        [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
            ErrorMessage = "Email is is not valid.")]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
        [ScaffoldColumn(false)]
        public decimal Total { get; set; }
        public List<OrderDetail> OrderDetails { get; set; }
    }
}

La tentative d’envoi du formulaire avec des informations manquantes ou non valides affiche désormais le message d’erreur à l’aide de la validation côté client.

Capture d’écran de la fenêtre Du Magasin de musique montrant l’adresse et la vue de paiement avec une chaîne d’informations non valides dans les champs téléphone et e-mail.

Ok, nous avons fait la plupart du travail dur pour le processus de paiement; nous avons juste quelques chances et se terminent. Nous devons ajouter deux vues simples, et nous devons nous occuper de la remise des informations du panier pendant le processus de connexion.

Ajout de la vue Extraction terminée

La vue Caisse terminée est assez simple, car il suffit d’afficher l’ID de commande. Cliquez avec le bouton droit sur l’action Terminer le contrôleur et ajoutez une vue nommée Complete qui est fortement tapée en tant que int.

Capture d’écran de la fenêtre Ajouter une vue avec le champ Nom de l’affichage et la liste déroulante Classe de modèle mis en évidence dans des rectangles rouges.

Nous allons maintenant mettre à jour le code d’affichage pour afficher l’ID de commande, comme indiqué ci-dessous.

@model int
@{
    ViewBag.Title = "Checkout Complete";
}
<h2>Checkout Complete</h2>
<p>Thanks for your order! Your order number is: @Model</p>
<p>How about shopping for some more music in our 
    @Html.ActionLink("store",
"Index", "Home")
</p>

Mise à jour de la vue d’erreur

Le modèle par défaut inclut une vue d’erreur dans le dossier Affichages partagés afin qu’il puisse être réutilisé ailleurs dans le site. Cette vue d’erreur contient une erreur très simple et n’utilise pas notre disposition de site. Nous allons donc la mettre à jour.

Comme il s’agit d’une page d’erreur générique, le contenu est très simple. Nous allons inclure un message et un lien pour accéder à la page précédente dans l’historique si l’utilisateur souhaite réessayer son action.

@{
    ViewBag.Title = "Error";
}
 
<h2>Error</h2>
 
<p>We're sorry, we've hit an unexpected error.
    <a href="javascript:history.go(-1)">Click here</a> 
    if you'd like to go back and try that again.</p>