Sdílet prostřednictvím


Část 9: Registrace a pokladna

Jon Galloway

MVC Music Store je výuková aplikace, která představuje a vysvětluje podrobný postup použití ASP.NET MVC a sady Visual Studio pro vývoj pro web.

MVC Music Store je odlehčená ukázková implementace, která prodává hudební alba online a implementuje základní správu webu, přihlašování uživatelů a funkce nákupního košíku.

Tato série kurzů podrobně popisuje všechny kroky k vytvoření ukázkové aplikace ASP.NET MVC Music Store. Část 9 se zabývá registrací a pokladnou.

V této části vytvoříme CheckoutController, který bude shromažďovat adresu a platební údaje nakupujícího. Budeme vyžadovat, aby se uživatelé před odhlášením zaregistrovali na našem webu, takže tento kontroler bude vyžadovat autorizaci.

Uživatelé přejdou na proces placení ze svého nákupního košíku kliknutím na tlačítko Pokladna.

Snímek obrazovky s oknem Obchodu s hudbou se zobrazením rezervace s tlačítkem Rezervovat zvýrazněným červenou šipkou

Pokud uživatel není přihlášený, zobrazí se výzva.

Snímek obrazovky s oknem Obchodu s hudbou zobrazující přihlašovací zobrazení s poli Uživatelské jméno a Heslo

Po úspěšném přihlášení se uživateli zobrazí zobrazení Adresa a platba.

Snímek obrazovky s oknem Obchodu s hudbou zobrazující adresu a zobrazení plateb s poli pro shromáždění dodací adresy a platebních údajů

Jakmile vyplní formulář a odešle objednávku, zobrazí se obrazovka s potvrzením objednávky.

Snímek obrazovky s oknem Obchodu s hudbou se zobrazením dokončení rezervace, které uživatele informuje o dokončení objednávky

Při pokusu o zobrazení neexistující objednávky nebo objednávky, která nepatří vám, se zobrazí zobrazení Chyba.

Snímek obrazovky s oknem Obchodu s hudbou zobrazující chybové zobrazení, když se uživatel pokusí zobrazit objednávku jiného uživatele nebo fiktivní objednávku

Migrace nákupního košíku

Když je proces nákupu anonymní, uživatel se po kliknutí na tlačítko Pokladna bude muset zaregistrovat a přihlásit. Uživatelé budou očekávat, že mezi návštěvami budeme uchovávat informace o nákupním košíku, takže informace o nákupním košíku budeme muset přidružit k uživateli, když dokončí registraci nebo přihlášení.

To je ve skutečnosti velmi jednoduché, protože naše třída ShoppingCart už má metodu, která přidruží všechny položky v aktuálním košíku s uživatelským jménem. Tuto metodu budeme muset volat, až uživatel dokončí registraci nebo přihlášení.

Otevřete třídu AccountController , kterou jsme přidali při nastavování členství a autorizace. Přidejte příkaz using odkazující na MvcMusicStore.Models a pak přidejte následující metodu MigrateShoppingCart:

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

Dále po ověření uživatele upravte akci LogOn tak, aby volala MigrateShoppingCart, jak je znázorněno níže:

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

Okamžitě po úspěšném vytvoření uživatelského účtu proveďte stejnou změnu akce Zaregistrovat příspěvek:

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

A je to – anonymní nákupní košík se teď po úspěšné registraci nebo přihlášení automaticky převede na uživatelský účet.

Vytvoření checkoutControlleru

Klikněte pravým tlačítkem na složku Kontrolery a pomocí šablony Prázdný kontroler přidejte nový kontroler do projektu s názvem CheckoutController.

Snímek obrazovky okna Přidat kontroler s polem Název kontroleru vyplněným textem Checkout Controller (Kontrola kontroleru)

Nejprve přidejte atribut Authorize nad deklaraci třídy Controller, aby se uživatelé před provedením rezervace zaregistrovali:

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

Poznámka: Je to podobné změně, kterou jsme dříve provedli u StoreManagerController, ale v takovém případě atribut Authorize vyžadoval, aby uživatel měl roli správce. V kontroleru rezervace vyžadujeme, aby byl uživatel přihlášený, ale nevyžadujeme, aby byl správcem.

Kvůli jednoduchosti se v tomto kurzu nebudeme zabývat platebními údaji. Místo toho uživatelům umožňujeme, aby se k pokladně chytli pomocí propagačního kódu. Tento propagační kód uložíme pomocí konstanty s názvem PromoCode.

Stejně jako u StoreController deklarujeme pole, které bude obsahovat instanci třídy MusicStoreEntities s názvem storeDB. Aby bylo možné využít MusicStoreEntities třídy, budeme muset přidat příkaz using pro obor názvů MvcMusicStore.Models. Horní část kontroleru Checkout se zobrazí níže.

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 bude obsahovat následující akce kontroleru:

AddressAndPayment (metoda GET) zobrazí formulář, který uživateli umožní zadat informace.

AddressAndPayment (metoda POST) ověří vstup a zpracuje objednávku.

Dokončeno se zobrazí po úspěšném dokončení procesu rezervace uživatelem. Toto zobrazení bude jako potvrzení obsahovat číslo objednávky uživatele.

Nejprve přejmenujme akci kontroleru indexu (která se vygenerovala při vytvoření kontroleru) na AddressAndPayment. Tato akce kontroleru jenom zobrazí formulář rezervace, takže nevyžaduje žádné informace o modelu.

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

Naše metoda AddressAndPayment POST se bude řídit stejným vzorem, jaký jsme použili v ovládacím panelu StoreManagerController: pokusí se přijmout odeslání formuláře a dokončit objednávku, a pokud selže, formulář se znovu zobrazí.

Po ověření, že vstup formuláře splňuje naše požadavky na ověření objednávky, zkontrolujeme přímo hodnotu formuláře PromoCode. Za předpokladu, že je vše v pořádku, uložíme aktualizované informace s objednávkou, sdělíme objektu ShoppingCart, aby proces objednávky dokončil, a přesměrujeme na akci Dokončit.

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

Po úspěšném dokončení procesu rezervace budou uživatelé přesměrováni na akci Dokončit kontroler. Tato akce provede jednoduchou kontrolu, která před zobrazením čísla objednávky jako potvrzení ověří, že objednávka skutečně patří přihlášeným uživatelům.

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

Poznámka: Zobrazení Chyba se pro nás automaticky vytvořilo ve složce /Views/Shared při zahájení projektu.

Úplný kód CheckoutController je následující:

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

Přidání zobrazení AddressAndPayment

Teď vytvoříme zobrazení AddressAndPayment. Klikněte pravým tlačítkem na jednu z akcí správce AddressAndPayment a přidejte zobrazení s názvem AddressAndPayment, které je silného typu Jako Objednávka a používá šablonu Upravit, jak je znázorněno níže.

Snímek obrazovky s oknem Přidat zobrazení s polem Název zobrazení, zaškrtávacím políkem Vytvořit zobrazení a červeně zvýrazněnými rozevíracími seznamy Třída modelu a Vygenerované uživatelské rozhraní

V tomto zobrazení se použijí dvě techniky, na které jsme se dívali při vytváření zobrazení StoreManagerEdit:

  • K zobrazení polí formuláře pro model objednávky použijeme Html.EditorForModel().
  • Budeme využívat ověřovací pravidla pomocí třídy Order s ověřovacími atributy.

Začneme aktualizací kódu formuláře tak, aby používal Html.EditorForModel(), následované dalším textovým polem pro propagační kód. Úplný kód pro zobrazení AddressAndPayment je zobrazený níže.

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

Definování ověřovacích pravidel pro objednávku

Teď, když je naše zobrazení nastavené, nastavíme ověřovací pravidla pro model objednávek stejně jako předtím pro model Album. Klikněte pravým tlačítkem na složku Models (Modely) a přidejte třídu s názvem Order (Objednávka). Kromě ověřovacích atributů, které jsme použili dříve pro album, budeme také používat regulární výraz k ověření e-mailové adresy uživatele.

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

Při pokusu o odeslání formuláře s chybějícími nebo neplatnými informacemi se teď zobrazí chybová zpráva při ověřování na straně klienta.

Snímek obrazovky s oknem Obchodu s hudbou zobrazující adresu a zobrazení plateb s řetězcem neplatných informací v polích pro telefon a e-mail

Dobře, udělali jsme většinu těžké práce na pokladně; máme jen pár pravděpodobností a končíme. Potřebujeme přidat dvě jednoduchá zobrazení a musíme se postarat o předání informací o košíku během procesu přihlášení.

Přidání zobrazení Dokončení rezervace

Zobrazení Dokončení rezervace je poměrně jednoduché, stačí jenom zobrazit ID objednávky. Klikněte pravým tlačítkem na akci Dokončit kontroler a přidejte zobrazení s názvem Dokončeno, které je silného typu jako int.

Snímek obrazovky okna Přidat zobrazení s polem Název zobrazení a rozevíracím seznamem Třída modelu zvýrazněnými červenými obdélníky

Teď aktualizujeme kód zobrazení tak, aby zobrazoval ID objednávky, jak je znázorněno níže.

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

Aktualizuje se zobrazení chyb

Výchozí šablona obsahuje zobrazení Chyba ve složce Sdílená zobrazení, aby ji bylo možné znovu použít jinde na webu. Toto zobrazení chyb obsahuje velmi jednoduchou chybu a nepoužívá rozložení webu, takže ho aktualizujeme.

Vzhledem k tomu, že se jedná o obecnou chybovou stránku, je obsah velmi jednoduchý. Pokud chce uživatel akci opakovat, zahrneme zprávu a odkaz pro přechod na předchozí stránku v historii.

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