Część 9. Rejestracja i finalizacja zakupu
Autor: Jon Galloway
Sklep MVC Music Store to aplikacja samouczka, która wprowadza i wyjaśnia krok po kroku, jak używać ASP.NET MVC i Visual Studio na potrzeby tworzenia aplikacji internetowych.
MVC Music Store to uproszczona przykładowa implementacja sklepu, która sprzedaje albumy muzyczne online i implementuje podstawową administrację witryną, logowanie użytkowników i funkcjonalność koszyka zakupów.
W tej serii samouczków szczegółowo przedstawiono wszystkie kroki, które należy wykonać w celu utworzenia przykładowej aplikacji ASP.NET MVC Music Store. Część 9 obejmuje rejestrację i wyewidencjonowania.
W tej sekcji utworzymy kontroler checkoutcontroller, który będzie zbierać adres i informacje o płatności kupujących. Będziemy wymagać od użytkowników zarejestrowania się w naszej witrynie przed wyewidencjonowywaniem, więc ten kontroler będzie wymagał autoryzacji.
Użytkownicy będą przechodzić do procesu wyewidencjonowania z koszyka zakupowego, klikając przycisk "Wyewidencjonuj".
Jeśli użytkownik nie jest zalogowany, zostanie wyświetlony monit.
Po pomyślnym zalogowaniu użytkownik jest następnie wyświetlany w widoku Adres i Płatność.
Po wypełnieniu formularza i przesłaniu zamówienia zostaną one wyświetlone na ekranie potwierdzenia zamówienia.
Próba wyświetlenia nieistniejącej kolejności lub zamówienia, które nie należy do Ciebie, spowoduje wyświetlenie widoku Błąd.
Migrowanie koszyka
Gdy proces zakupów jest anonimowy, gdy użytkownik kliknie przycisk Wyewidencjonuj, będzie musiał się zarejestrować i zalogować. Użytkownicy będą oczekiwać, że będziemy utrzymywać informacje o koszyku zakupów między wizytami, dlatego będziemy musieli skojarzyć informacje o koszyku z użytkownikiem po zakończeniu rejestracji lub zalogowania.
Jest to faktycznie bardzo proste, ponieważ nasza klasa ShoppingCart ma już metodę, która skojarzy wszystkie elementy w bieżącym koszyku z nazwą użytkownika. Wystarczy wywołać tę metodę, gdy użytkownik ukończy rejestrację lub identyfikator logowania.
Otwórz klasę AccountController dodaną podczas konfigurowania członkostwa i autoryzacji. Dodaj instrukcję using odwołującą się do mvcMusicStore.Models, a następnie dodaj następującą metodę 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;
}
Następnie zmodyfikuj akcję wpisu LogOn, aby wywołać obiekt MigrateShoppingCart po zweryfikowaniu użytkownika, jak pokazano poniżej:
//
// 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);
}
Wprowadź tę samą zmianę w akcji Zarejestruj wpis natychmiast po pomyślnym utworzeniu konta użytkownika:
//
// 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);
}
To wszystko — teraz anonimowy koszyk na zakupy zostanie automatycznie przeniesiony na konto użytkownika po pomyślnej rejestracji lub zalogowaniu.
Tworzenie kontrolera CheckoutController
Kliknij prawym przyciskiem myszy folder Controllers i dodaj nowy kontroler do projektu o nazwie CheckoutController przy użyciu szablonu Pusty kontroler.
Najpierw dodaj atrybut Authorize powyżej deklaracji klasy Controller, aby wymagać od użytkowników zarejestrowania się przed wyewidencjonowania:
namespace MvcMusicStore.Controllers
{
[Authorize]
public class CheckoutController : Controller
Uwaga: jest to podobne do zmiany wprowadzonej wcześniej w StoreManagerController, ale w takim przypadku atrybut Authorize wymaga, aby użytkownik był w roli Administrator. W kontrolerze wyewidencjonowania wymagane jest zalogowanie użytkownika, ale nie wymaga, aby byli administratorami.
Dla uproszczenia nie będziemy mieć do czynienia z informacjami o płatności w tym samouczku. Zamiast tego pozwalamy użytkownikom na wyewidencjonowanie przy użyciu kodu promocyjnego. Będziemy przechowywać ten kod promocyjny przy użyciu stałej o nazwie PromoCode.
Podobnie jak w storeController, zadeklarujemy pole do przechowywania wystąpienia klasy MusicStoreEntities o nazwie storeDB. Aby móc korzystać z klasy MusicStoreEntities, należy dodać instrukcję using dla przestrzeni nazw MvcMusicStore.Models. Poniżej znajduje się górna część kontrolera wyewidencjonowania.
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";
Kontroler wyewidencjonowania będzie miał następujące akcje kontrolera:
Metoda AddressAndPayment (GET) wyświetli formularz umożliwiający użytkownikowi wprowadzanie informacji.
AddressAndPayment (metoda POST) zweryfikuje dane wejściowe i przetworzy zamówienie.
Ukończenie zostanie wyświetlone po pomyślnym zakończeniu procesu wyewidencjonowania przez użytkownika. Ten widok będzie zawierać numer zamówienia użytkownika jako potwierdzenie.
Najpierw zmieńmy nazwę akcji kontrolera indeksu (która została wygenerowana podczas tworzenia kontrolera) na AddressAndPayment. Ta akcja kontrolera po prostu wyświetla formularz wyewidencjonowania, więc nie wymaga żadnych informacji o modelu.
//
// GET: /Checkout/AddressAndPayment
public ActionResult AddressAndPayment()
{
return View();
}
Nasza metoda AddressAndPayment POST będzie działać zgodnie z tym samym wzorcem, którego użyliśmy w StoreManagerController: podejmie próbę zaakceptowania przesłania formularza i ukończenia zamówienia i ponownie wyświetli formularz, jeśli zakończy się niepowodzeniem.
Po zweryfikowaniu danych wejściowych formularza spełnia nasze wymagania dotyczące walidacji zamówienia, sprawdzimy bezpośrednio wartość formularza PromoCode. Zakładając, że wszystko jest poprawne, zapiszemy zaktualizowane informacje z zamówieniem, poinformuj obiekt ShoppingCart, aby ukończyć proces zamówienia, i przekierowujemy do akcji Zakończ.
//
// 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 pomyślnym zakończeniu procesu wyewidencjonowania użytkownicy zostaną przekierowani do akcji Zakończ kontroler. Ta akcja spowoduje wykonanie prostego sprawdzenia, czy zamówienie rzeczywiście należy do zalogowanego użytkownika przed wyświetleniem numeru zamówienia jako potwierdzenia.
//
// 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");
}
}
Uwaga: Widok błędu został automatycznie utworzony dla nas w folderze /Views/Shared po rozpoczęciu projektu.
Kompletny kod CheckoutController jest następujący:
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");
}
}
}
}
Dodawanie widoku AddressAndPayment
Teraz utwórzmy widok AddressAndPayment. Kliknij prawym przyciskiem myszy jedną z akcji kontrolera AddressAndPayment i dodaj widok o nazwie AddressAndPayment, który jest silnie typizowane jako zamówienie i używa szablonu Edytuj, jak pokazano poniżej.
Ten widok będzie korzystał z dwóch technik, które omówiliśmy podczas kompilowania widoku StoreManagerEdit:
- Użyjemy metody Html.EditorForModel() do wyświetlania pól formularza dla modelu Order
- Użyjemy reguł weryfikacji przy użyciu klasy Order z atrybutami weryfikacji
Zaczniemy od zaktualizowania kodu formularza w celu użycia metody Html.EditorForModel(), a następnie dodatkowego pola tekstowego kodu promocyjnego. Poniżej przedstawiono kompletny kod widoku 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" />
}
Definiowanie reguł walidacji dla zamówienia
Teraz, po skonfigurowaniu widoku, skonfigurujemy reguły walidacji dla naszego modelu Zamówienia, tak jak wcześniej dla modelu Album. Kliknij prawym przyciskiem myszy folder Models i dodaj klasę o nazwie Order. Oprócz atrybutów weryfikacji użytych wcześniej dla albumu będziemy również używać wyrażenia regularnego do sprawdzania poprawności adresu e-mail użytkownika.
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; }
}
}
Próba przesłania formularza z brakującymi lub nieprawidłowymi informacjami spowoduje teraz wyświetlenie komunikatu o błędzie przy użyciu weryfikacji po stronie klienta.
Dobrze, wykonaliśmy większość ciężkiej pracy dla procesu wyewidencjonowania; Mamy tylko kilka kursów i kończy się do końca. Musimy dodać dwa proste widoki i musimy zadbać o przekazanie informacji o koszyku podczas procesu logowania.
Dodawanie widoku Finalizacja wyewidencjonowania
Widok Finalizacja wyewidencjonowania jest dość prosty, ponieważ wystarczy wyświetlić identyfikator zamówienia. Kliknij prawym przyciskiem myszy akcję Zakończ kontroler i dodaj widok o nazwie Ukończ, który jest silnie typizowane jako int.
Teraz zaktualizujemy kod widoku, aby wyświetlić identyfikator zamówienia, jak pokazano poniżej.
@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>
Aktualizowanie widoku błędu
Szablon domyślny zawiera widok Błąd w folderze Widoki udostępnione, aby można było go ponownie używać w innym miejscu w witrynie. Ten widok błędu zawiera bardzo prosty błąd i nie używa naszego układu witryny, dlatego zaktualizujemy go.
Ponieważ jest to ogólna strona błędu, zawartość jest bardzo prosta. Dołączymy komunikat i link umożliwiający przejście do poprzedniej strony w historii, jeśli użytkownik chce ponowić próbę wykonania akcji.
@{
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>
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla