9부: 등록 및 체크 아웃
MVC Music Store는 웹 개발을 위해 ASP.NET MVC 및 Visual Studio를 사용하는 방법을 단계별로 소개하고 설명하는 자습서 애플리케이션입니다.
MVC Music Store는 온라인으로 음악 앨범을 판매하고 기본 사이트 관리, 사용자 로그인 및 쇼핑 카트 기능을 구현하는 간단한 샘플 저장소 구현입니다.
이 자습서 시리즈에서는 ASP.NET MVC Music Store 샘플 애플리케이션을 빌드하기 위해 수행되는 모든 단계를 자세히 설명합니다. 9부에서는 등록 및 체크 아웃을 다룹니다.
이 섹션에서는 쇼핑객의 주소 및 결제 정보를 수집하는 CheckoutController를 만듭니다. 사용자가 체크 아웃하기 전에 사이트에 등록해야 하므로 이 컨트롤러에는 권한 부여가 필요합니다.
사용자는 "체크 아웃" 단추를 클릭하여 쇼핑 카트에서 체크 아웃 프로세스로 이동합니다.
사용자가 로그인하지 않은 경우 사용자에게 메시지가 표시됩니다.
로그인에 성공하면 사용자에게 주소 및 결제 보기가 표시됩니다.
양식을 작성하고 주문을 제출하면 주문 확인 화면이 표시됩니다.
존재하지 않는 순서 또는 에 속하지 않는 순서를 보려고 하면 오류 보기가 표시됩니다.
쇼핑 카트 마이그레이션
쇼핑 프로세스는 익명이지만 사용자가 체크 아웃 단추를 클릭하면 등록하고 로그인해야 합니다. 사용자는 방문 사이에 쇼핑 카트 정보를 유지 관리하므로 등록 또는 로그인을 완료할 때 쇼핑 카트 정보를 사용자와 연결해야 합니다.
이 작업은 실제로 매우 간단합니다. ShoppingCart 클래스에는 현재 카트의 모든 항목을 사용자 이름과 연결하는 메서드가 이미 있습니다. 사용자가 등록 또는 로그인을 완료할 때 이 메서드를 호출하면 됩니다.
멤버 자격 및 권한 부여를 설정할 때 추가한 AccountController 클래스를 엽니다. MvcMusicStore.Models를 참조하는 using 문을 추가한 다음, 다음 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;
}
다음으로, 아래와 같이 사용자의 유효성을 검사한 후 MigrateShoppingCart를 호출하도록 LogOn 게시 작업을 수정합니다.
//
// 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 만들기
Controllers 폴더를 마우스 오른쪽 단추로 클릭하고 빈 컨트롤러 템플릿을 사용하여 CheckoutController라는 프로젝트에 새 컨트롤러를 추가합니다.
먼저 컨트롤러 클래스 선언 위에 Authorize 특성을 추가하여 체크 아웃 전에 사용자가 등록하도록 요구합니다.
namespace MvcMusicStore.Controllers
{
[Authorize]
public class CheckoutController : Controller
참고: 이는 이전에 StoreManagerController를 변경한 것과 유사하지만, 이 경우 Authorize 특성은 사용자가 관리자 역할에 있어야 합니다. 체크 아웃 컨트롤러에서 사용자에게 로그인을 요구하지만 관리자가 될 필요는 없습니다.
간단히 하기 위해 이 자습서에서는 결제 정보를 다루지 않습니다. 대신 사용자가 홍보 코드를 사용하여 검사 수 있습니다. PromoCode라는 상수를 사용하여 이 홍보 코드를 저장합니다.
StoreController에서와 같이 storeDB라는 MusicStoreEntities 클래스의 instance 저장할 필드를 선언합니다. MusicStoreEntities 클래스를 사용하려면 MvcMusicStore.Models 네임스페이스에 대한 using 문을 추가해야 합니다. 체크 아웃 컨트롤러의 맨 위가 아래에 표시됩니다.
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 메서드) 는 입력의 유효성을 검사하고 주문을 처리합니다.
완료 는 사용자가 체크 아웃 프로세스를 성공적으로 완료한 후에 표시됩니다. 이 보기에는 사용자의 주문 번호가 확인으로 포함됩니다.
먼저 컨트롤러를 만들 때 생성된 인덱스 컨트롤러 작업의 이름을 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 컨트롤러 작업 중 하나를 마우스 오른쪽 단추로 클릭하고 Order로 강력하게 입력되고 아래와 같이 편집 템플릿을 사용하는 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; }
}
}
누락되거나 잘못된 정보가 있는 양식을 제출하려고 하면 이제 클라이언트 쪽 유효성 검사를 사용하여 오류 메시지가 표시됩니다.
체크 아웃 프로세스에 대한 대부분의 작업을 수행했습니다. 우리는 단지 몇 가지 확률을 가지고 완료 종료합니다. 두 개의 간단한 보기를 추가해야 하며 로그인 프로세스 중에 카트 정보의 전달을 처리해야 합니다.
체크 아웃 완료 보기 추가
체크 아웃 완료 보기는 주문 ID를 표시하기만 하면 되므로 매우 간단합니다. 컨트롤러 완료 작업을 마우스 오른쪽 단추로 클릭하고 int로 강력하게 입력된 완료라는 보기를 추가합니다.
이제 아래와 같이 주문 ID를 표시하도록 보기 코드를 업데이트합니다.
@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>