Часть 9. Регистрация и оформление заказа
Хранилище музыки MVC — это учебное приложение, в которое пошаговые инструкции по использованию ASP.NET MVC и Visual Studio для веб-разработки.
MVC Music Store — это упрощенный пример реализации магазина, который продает музыкальные альбомы в Интернете и реализует базовые функции администрирования сайтов, входа пользователей и корзины для покупок.
В этой серии учебников подробно описаны все действия по созданию примера приложения ASP.NET MVC Music Store. В части 9 рассматриваются регистрация и оформление заказа.
В этом разделе мы создадим CheckoutController, который будет собирать адрес и платежные данные покупателя. Мы будем требовать от пользователей регистрации на нашем сайте перед извлечением, поэтому этому контроллеру потребуется авторизация.
Пользователи переходят к процессу оформления заказа из своей корзины, нажав кнопку "Извлечь".
Если пользователь не вошел в систему, ей будет предложено.
После успешного входа пользователю отображается представление Адрес и Платеж.
После заполнения формы и отправки заказа появится экран подтверждения заказа.
При попытке просмотреть несуществующий заказ или порядок, который не принадлежит вам, отобразится представление Ошибка.
Перенос корзины покупок
В то время как процесс покупки является анонимным, когда пользователь нажимает кнопку Оформления заказа, он должен будет зарегистрироваться и войти в систему. Пользователи ожидают, что мы будем поддерживать их информацию о корзине покупок между посещениями, поэтому нам потребуется связать данные корзины с пользователем после завершения регистрации или входа.
На самом деле это очень просто сделать, так как наш класс ShoppingCart уже имеет метод, который свяжет все элементы в текущей корзине с именем пользователя. Нам нужно будет просто вызвать этот метод, когда пользователь завершает регистрацию или вход.
Откройте класс AccountController , добавленный при настройке членства и авторизации. Добавьте оператор using, ссылающийся на MvcMusicStore.Models, а затем добавьте следующий метод 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;
}
Затем измените действие logOn post, чтобы вызвать MigrateShoppingCart после проверки пользователя, как показано ниже:
//
// 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);
}
Внесите те же изменения в действие Регистрация после успешного создания учетной записи пользователя:
//
// 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);
}
Вот и все . Теперь анонимная корзина покупок будет автоматически перенесена в учетную запись пользователя после успешной регистрации или входа.
Создание CheckoutController
Щелкните правой кнопкой мыши папку Контроллеры и добавьте новый контроллер в проект CheckoutController с помощью шаблона Пустой контроллер.
Сначала добавьте атрибут Authorize над объявлением класса Controller, чтобы требовать от пользователей регистрации перед оформлением заказа:
namespace MvcMusicStore.Controllers
{
[Authorize]
public class CheckoutController : Controller
Примечание. Это похоже на изменение, внесенное ранее в StoreManagerController, но в этом случае атрибут Authorize требует, чтобы пользователь был в роли администратора. В контроллере извлечения мы требуем, чтобы пользователь вошел в систему, но не требует, чтобы он был администратором.
Для простоты мы не будем иметь дело с платежными данными в этом руководстве. Вместо этого мы разрешаем пользователям проверка с помощью рекламного кода. Мы будем хранить этот рекламный код с помощью константы с именем PromoCode.
Как и в StoreController, мы объявим поле для хранения экземпляра класса MusicStoreEntities с именем storeDB. Чтобы использовать класс MusicStoreEntities, необходимо добавить оператор using для пространства имен MvcMusicStore.Models. Ниже отображается верхняя часть контроллера Checkout.
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 будет выполнять следующие действия контроллера:
AddressAndPayment (метод GET) отобразит форму, чтобы разрешить пользователю вводить свои данные.
AddressAndPayment (метод POST) проверит входные данные и обработает заказ.
После того, как пользователь успешно завершит процесс оформления заказа, отобразится значение Complete. Это представление будет включать номер заказа пользователя в качестве подтверждения.
Сначала переименуем действие контроллера индекса (которое было создано при создании контроллера) в AddressAndPayment. Это действие контроллера просто отображает форму оформления заказа, поэтому ему не требуются никакие сведения о модели.
//
// GET: /Checkout/AddressAndPayment
public ActionResult AddressAndPayment()
{
return View();
}
Наш метод AddressAndPayment POST будет следовать тому же шаблону, который использовался в StoreManagerController: он попытается принять отправку формы и завершить заказ, а в случае сбоя форма будет отображаться повторно.
После проверки того, что входные данные формы соответствуют нашим требованиям к проверке заказа, мы проверка значение формы PromoCode напрямую. Если все правильно, мы сохраним обновленную информацию вместе с заказом, сообщите объекту ShoppingCart о завершении процесса заказа и перенаправимся к действию Завершить.
//
// 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");
}
}
Примечание. Представление ошибок было автоматически создано для нас в папке /Views/Shared при запуске проекта.
Полный код CheckoutController выглядит следующим образом:
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");
}
}
}
}
Добавление представления AddressAndPayment
Теперь создадим представление AddressAndPayment. Щелкните правой кнопкой мыши одно из действий контроллера AddressAndPayment и добавьте представление с именем AddressAndPayment, которое строго типизировано как Заказ и использует шаблон Изменить, как показано ниже.
В этом представлении будут использоваться два метода, которые мы рассмотрели при создании представления StoreManagerEdit:
- Мы будем использовать Html.EditorForModel() для отображения полей формы для модели заказов.
- Мы будем использовать правила проверки с помощью класса Order с атрибутами проверки
Сначала мы обновим код формы для использования Html.EditorForModel(), а затем добавим текстовое поле для промокода. Полный код представления 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" />
}
Определение правил проверки для заказа
Теперь, когда наше представление настроено, мы настроим правила проверки для модели Заказов, как и ранее для модели альбомов. Щелкните правой кнопкой мыши папку Models и добавьте класс с именем Order. В дополнение к атрибутам проверки, которые мы использовали ранее для альбома, мы также будем использовать регулярное выражение для проверки адреса электронной почты пользователя.
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; }
}
}
При попытке отправить форму с отсутствующими или недопустимыми сведениями будет отображаться сообщение об ошибке при проверке на стороне клиента.
Хорошо, мы проделали большую часть напряженной работы для процесса оформления заказа. У нас просто есть несколько шансов и заканчивается, чтобы закончить. Нам нужно добавить два простых представления и позаботиться о том, чтобы передать информацию о корзине во время входа.
Добавление представления "Завершение оформления заказа"
Представление Checkout Complete довольно простое, так как в нем просто должен отображаться идентификатор заказа. Щелкните правой кнопкой мыши действие контроллера Завершить и добавьте представление с именем Завершить, которое строго типизировано как int.
Теперь мы обновим код представления для отображения идентификатора заказа, как показано ниже.
@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>
Обновление представления ошибок
Шаблон по умолчанию содержит представление "Ошибка" в папке "Общие представления", чтобы его можно было повторно использовать в другом месте сайта. Это представление ошибки содержит очень простую ошибку и не использует наш макет сайта, поэтому мы обновим его.
Так как это общая страница ошибок, содержимое очень простое. Мы добавим сообщение и ссылку для перехода на предыдущую страницу в журнале, если пользователь хочет повторить свое действие.
@{
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>