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 ».
Si l’utilisateur n’est pas connecté, il est invité à le faire.
Une fois la connexion réussie, la vue Adresse et Paiement s’affiche à l’utilisateur.
Une fois qu’ils ont rempli le formulaire et envoyé la commande, l’écran de confirmation de commande s’affiche.
Si vous tentez d’afficher une commande inexistante ou une commande qui n’appartient pas à vous, la vue Erreur s’affiche.
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.
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.
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.
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.
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>