Condividi tramite


Parte 9: Registrazione e completamento della transazione

di Jon Galloway

MVC Music Store è un'applicazione di esercitazione che introduce e spiega in modo dettagliato come usare ASP.NET MVC e Visual Studio per lo sviluppo Web.

MVC Music Store è un'implementazione di negozio di esempio leggera che vende album musicali online e implementa l'amministrazione del sito di base, l'accesso utente e la funzionalità del carrello acquisti.

Questa serie di esercitazioni illustra in dettaglio tutti i passaggi eseguiti per compilare l'applicazione di esempio MVC Music Store ASP.NET. La parte 9 riguarda la registrazione e il pagamento.

In questa sezione verrà creato un CheckoutController che raccoglierà l'indirizzo e le informazioni di pagamento dell'acquirente. Gli utenti dovranno registrarsi con il sito prima del check-out, quindi questo controller richiederà l'autorizzazione.

Gli utenti passeranno al processo di pagamento dal carrello acquisti facendo clic sul pulsante "Checkout".

Screenshot della finestra di Music Store che mostra la visualizzazione di pagamento con il pulsante Checkout evidenziato da una freccia rossa.

Se l'utente non è connesso, verrà richiesto.

Screenshot della finestra di Music Store che mostra la visualizzazione di accesso con il nome utente e i campi Password.

Al termine dell'accesso, l'utente viene quindi visualizzato la visualizzazione Indirizzo e pagamento.

Screenshot della finestra di Music Store che mostra l'indirizzo e la vista di pagamento con i campi per raccogliere l'indirizzo di spedizione e le informazioni di pagamento.

Dopo aver compilato il modulo e inviato l'ordine, verrà visualizzata la schermata di conferma dell'ordine.

Screenshot della finestra di Music Store che mostra la visualizzazione completa del checkout che informa l'utente che l'ordine è stato completato.

Il tentativo di visualizzare un ordine non esistente o un ordine che non appartiene all'utente visualizzerà la visualizzazione Errore.

Screenshot della finestra di Music Store che mostra la visualizzazione degli errori quando l'utente tenta di visualizzare l'ordine di un'altra persona o un ordine fittizio.

Migrazione del carrello acquisti

Mentre il processo di acquisto è anonimo, quando l'utente fa clic sul pulsante Checkout, sarà necessario registrare e accedere. Gli utenti si aspettano che manterrà le informazioni del carrello acquisti tra visite, quindi sarà necessario associare le informazioni del carrello acquisti a un utente quando completano la registrazione o l'accesso.

Questo è in realtà molto semplice da fare, perché la nostra classe ShoppingCart ha già un metodo che associa tutti gli elementi nel carrello corrente a un nome utente. Sarà sufficiente chiamare questo metodo quando un utente completa la registrazione o l'account di accesso.

Aprire la classe AccountController aggiunta durante la configurazione dell'appartenenza e dell'autorizzazione. Aggiungere un'istruzione using che fa riferimento a MvcMusicStore.Models, quindi aggiungere il metodo MigrateShoppingCart seguente:

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

Modificare quindi l'azione post LogOn per chiamare MigrateShoppingCart dopo la convalida dell'utente, come illustrato di seguito:

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

Apportare la stessa modifica all'azione Registrazione post, immediatamente dopo la creazione dell'account utente:

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

Questo è: ora un carrello acquisti anonimo verrà trasferito automaticamente a un account utente dopo aver eseguito la registrazione o l'accesso.

Creazione di CheckoutController

Fare clic con il pulsante destro del mouse sulla cartella Controller e aggiungere un nuovo controller al progetto denominato CheckoutController usando il modello controller vuoto.

Screenshot della finestra Aggiungi controller con il campo Nome controller riempito con il testo Check Controller controller.

Aggiungere prima di tutto l'attributo Authorize sopra la dichiarazione della classe Controller per richiedere agli utenti di registrare prima del checkout:

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

Nota: è simile alla modifica apportata in precedenza a StoreManagerController, ma in questo caso l'attributo Authorize richiede che l'utente sia in un ruolo Amministratore. Nel controller di pagamento è necessario che l'utente sia connesso, ma non richiede che siano amministratori.

Per motivi di semplicità, non si tratta di informazioni di pagamento in questa esercitazione. È invece possibile che gli utenti usino un codice promozionale. Questo codice promozionale verrà archiviato usando una costante denominata PromoCode.

Come in StoreController, verrà dichiarato un campo per contenere un'istanza della classe MusicStoreEntities, denominata storeDB. Per usare la classe MusicStoreEntities, è necessario aggiungere un'istruzione using per lo spazio dei nomi MvcMusicStore.Models. La parte superiore del controller di pagamento viene visualizzata di seguito.

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

CheckoutController avrà le azioni del controller seguenti:

AddressAndPayment (metodo GET) visualizzerà un modulo per consentire all'utente di immettere le informazioni.

AddressAndPayment (metodo POST) convalida l'input ed elabora l'ordine.

Il completamento verrà visualizzato dopo che un utente ha completato correttamente il processo di pagamento. Questa visualizzazione includerà il numero di ordine dell'utente, come conferma.

In primo luogo, si rinomina l'azione del controller di indice (generata al momento della creazione del controller) in AddressAndPayment. Questa azione controller visualizza solo il modulo di pagamento, quindi non richiede alcuna informazione del modello.

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

Il metodo AddressAndPayment POST seguirà lo stesso modello usato in StoreManagerController: tenterà di accettare l'invio del modulo e di completare l'ordine e visualizzerà nuovamente il modulo se ha esito negativo.

Dopo aver convalidato l'input del modulo soddisfa i requisiti di convalida per un ordine, si verificherà direttamente il valore del modulo PromoCode. Supponendo che tutto sia corretto, verranno salvate le informazioni aggiornate con l'ordine, indicare all'oggetto ShoppingCart di completare il processo di ordine e reindirizzare all'azione Completa.

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

Al termine del processo di pagamento, gli utenti verranno reindirizzati all'azione Completa controller. Questa azione eseguirà un semplice controllo per verificare che l'ordine appartenga effettivamente all'utente connesso prima di visualizzare il numero di ordine come conferma.

//
// 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");
    }
}

Nota: la visualizzazione Errore è stata creata automaticamente per noi nella cartella /Views/Shared al momento dell'avvio del progetto.

Il codice CheckoutController completo è il seguente:

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

Aggiunta della visualizzazione AddressAndPayment

A questo punto, verrà creata la visualizzazione AddressAndPayment. Fare clic con il pulsante destro del mouse su una delle azioni del controller AddressAndPayment e aggiungere una visualizzazione denominata AddressAndPayment che è fortemente tipizzata come ordine e usa il modello Modifica, come illustrato di seguito.

Screenshot della finestra Aggiungi visualizzazione con il campo Nome visualizzazione, la casella di controllo Crea una visualizzazione e l'elenco a discesa Model e Scaffold evidenziati in rosso.

Questa visualizzazione userà due delle tecniche esaminate durante la creazione della visualizzazione StoreManagerEdit:

  • Verrà usato Html.EditorForModel() per visualizzare i campi del modulo per il modello Di ordine
  • Verranno usate le regole di convalida usando una classe Order con attributi di convalida

Si inizierà aggiornando il codice del modulo per usare Html.EditorForModel(), seguito da una casella di testo aggiuntiva per il codice promozionale. Di seguito è riportato il codice completo per la visualizzazione AddressAndPayment.

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

Definizione delle regole di convalida per l'ordine

Ora che la visualizzazione è configurata, verrà configurata la regola di convalida per il modello Di ordine come in precedenza per il modello Album. Fare clic con il pulsante destro del mouse sulla cartella Modelli e aggiungere una classe denominata Order. Oltre agli attributi di convalida usati in precedenza per l'album, verrà usata anche un'espressione regolare per convalidare l'indirizzo di posta elettronica dell'utente.

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

Il tentativo di inviare il modulo con informazioni mancanti o non valide visualizzerà ora il messaggio di errore usando la convalida lato client.

Screenshot della finestra di Music Store che mostra l'indirizzo e la visualizzazione di pagamento con una stringa di informazioni non valide nei campi telefono e posta elettronica.

Ok, abbiamo fatto la maggior parte del lavoro duro per il processo di pagamento; abbiamo solo qualche probabilità e termina per terminare. Dobbiamo aggiungere due semplici visualizzazioni e dobbiamo prendersi cura della consegna delle informazioni del carrello durante il processo di accesso.

Aggiunta della visualizzazione Completamento estrazione

La visualizzazione Completamento estrazione è piuttosto semplice, perché deve solo visualizzare l'ID ordine. Fare clic con il pulsante destro del mouse sull'azione Completa controller e aggiungere una visualizzazione denominata Complete che è fortemente tipizzata come int.

Screenshot della finestra Aggiungi visualizzazione con il campo Nome visualizzazione e l'elenco a discesa Classe modello evidenziato nei rettangoli rossi.

Verrà ora aggiornato il codice di visualizzazione per visualizzare l'ID ordine, come illustrato di seguito.

@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>

Aggiornamento della visualizzazione Errore

Il modello predefinito include una visualizzazione Errore nella cartella Visualizzazioni condivise in modo che possa essere riutilizzata altrove nel sito. Questa visualizzazione Errore contiene un errore molto semplice e non usa il layout del sito, quindi lo aggiorneremo.

Poiché si tratta di una pagina di errore generica, il contenuto è molto semplice. Verrà incluso un messaggio e un collegamento per passare alla pagina precedente nella cronologia se l'utente vuole riprovare l'azione.

@{
    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>