Část 9: Registrace a pokladna
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.
Pokud uživatel není přihlášený, zobrazí se výzva.
Po úspěšném přihlášení se uživateli zobrazí zobrazení Adresa a platba.
Jakmile vyplní formulář a odešle objednávku, zobrazí se obrazovka s potvrzením objednávky.
Při pokusu o zobrazení neexistující objednávky nebo objednávky, která nepatří vám, se zobrazí zobrazení Chyba.
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.
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.
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.
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.
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>