ASP.NET MVC 音乐商店 - 9. 注册和结账

转自http://www.cnblogs.com/haogj/archive/2011/11/20/2255675.html

 

在这一节,我们将创建结账的控制器 CheckoutController
来收集用户的地址和付款信息,我们需要用户在结账前注册账户,因为这个控制器需要授权。

当用户点击结账 Checkout 按钮的时候,用户将会被导航到结账的处理流程中。

如果用户没有登录,将会被提示需要登录。

一旦用户成功登陆,用户就可以看到地址和付款的视图。

一旦用户填写了这个表单并提交,他们将会看到订单的确认页面。

视图访问不存在的订单,或者不属于你的订单,将会看到错误页面。

合并购物车

在匿名购物的时候,当用户点击结账 Checkout
按钮,用户会被要求注册和登陆,用户会希望继续使用原来的购物车,所以,在匿名用户登录之后,我们需要维护购物车。

实际上非常简单,因为 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;}

然后,修改 LonOn 的 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);}

在 Register 的 Post 处理方法中,一旦用户成功创建帐户,也进行类似的修改,

//// 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 控制器

在 Controller 文件夹上右键,添加一个新的控制器,命名为 CheckoutController ,使用空的控制器模板。

首先,在控制器上增加授权的标注 [Authorize],来确定用户必须在登录之后才能访问。

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

注意:这一步很像我们前面在 StoreManager 控制器上的工作,但是,在那个时候,我们要求用户必须拥有 Administrator
的角色。在结账控制器中,我们不需要用户必须是 Administrator ,而是必须登录。

出于简化的考虑,在这个教程中没有处理付款的信息,作为替代,我们允许用户输入一个促销代码,这里促销代码定义在常量 PromoCode。

像在 StoreController 中一样,在控制器中,我们也需要定义 MusicStoreEntities 的字段,将它命名为
storeDB,结账的开始部分如下。

using MvcMusicStore.Models;

namespace MvcMusicStore.Controllers{    [Authorize]public class CheckoutController : Controller    {        MusicStoreEntities storeDB = new MusicStoreEntities();const string PromoCode = "FREE";

结账的控制器将包含下面的控制器方法:

AddressAndPayment ( Get ) 用来显示一个用户输入信息的表单

AddressAndPayment ( Post ) 验证用户的输入,处理订单。

Complete 用来在在用户完成订单之后显示,这个视图包含用户的订单账号和确认信息。

首先,将 Index 方法改名为 AddressAndPayment, 这个 Action 方法用来显示结账表单,所以,不需要任何的模型信息。

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

AddressAndPayment 的 Post 处理方法使用我们在 StoreManagerController
中类似的模式:如果成功了就完成订单,如果失败了就重新显示表单。

在验证了表单之后,我们将会直接检查促销代码,假设所有的信息都是正确的,我们将会在订单中保存信息,告诉购物车对象完成订单处理,最后,重定向到完成的
Complete Action 方法。

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

一旦完成了结账处理,用户将被重定向到 Complete 方法, 这个 Action
方法将会进行简单的检查,在显示订单号之前,检查订单是否属于当前登录的用户。

//// GET: /Checkout/Completepublic 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 文件夹中的 error.cshtml 生成。

完整的 CheckoutController 如下所示.

using System;using System.Collections.Generic;using System.Linq;using System.Web;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();        }

[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 控制器的某个 Action 中点击鼠标的右键,增加名为
AddressAndPayment 的强类型 Order 视图,使用编辑模板。

这个视图使用我们在 StoreManager 的 Edit 视图中使用的两个技术:

1.使用 Html.EditorForModel() 来显示订单模型的字段

2.使用 Order 模型的验证标签定义验证规则

我们先使用 Html.EditorForModel() 方法,然后,增加额外的输入框用来输入促销码,完成的视图如下。

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

在没有提交必要的信息或者提交错误信息的时候,我们将会看到客户端的验证信息。

现在,我们已经完成了结账中最困难的部分,还有一些小的工作要完成。我们增加两个简单的视图,还需要注意在登录过程中购物车信息。

增加完成结账视图

完成结账的视图非常简单,仅仅需要显示订单的编号,在控制器中的 Complete 方法上点击右键,增加名为 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>

更新错误视图

项目的默认模板中,包含了定义在 /Shared Views
文件夹中的错误页面,可以在整个站点中使用。这个页面仅仅包含简单的信息,也没有使用我们的布局,我们更新一下。

由于这是通用的错误页面,内容非常简单,我们仅仅包含一个提示信息,和用来重做工作的导航到上一个页面的链接。

@{ 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.
时间: 2024-10-29 09:43:42

ASP.NET MVC 音乐商店 - 9. 注册和结账的相关文章

ASP.NET MVC 音乐商店 - 3. 视图与模型

转自http://www.cnblogs.com/haogj/archive/2011/11/11/2244895.html 上一篇中有同学提到为什么不使用视图,而使用字符串,这一篇我们就开始使用视图来处理. 我们已经可以从控制器的 Action 中返回一个字符串,这可以帮助我们更好地理解 Controller 是如何工作的.但是对于创建一个 Web 程序来说还是不够的.下面我们使用更好的方法来生成 HTML,主要是通过模板来生成需要的 HTML,这就是视图所要做的.  增加视图模板 为了使用视

ASP.NET MVC 音乐商店 - 0 概览

转自http://www.cnblogs.com/haogj/archive/2011/11/08/2241710.html 这是一个系列文章,原文内容出自微软的 MusicStore. 首先对原文内容进行了简单的翻译,以方便大家参考,另外对于其中的部分内容,也进行了简单的分析,使用的 Visual Studio 也换成了中文版,这样大家看起来也更亲切一些. 下载地址:http://mvcmusicstore.codeplex.com/   MVC 音乐店是用来介绍和展示使用 ASP.NETMV

ASP.NET MVC 音乐商店 - 2.控制器

转自http://www.cnblogs.com/haogj/archive/2011/11/10/2241824.html 在典型的 Web 应用中,用户请求的 URL 地址通常映射到保存在网站中的文件上,例如,当用户请求 /Products.aspx 的时候,或者 /Products.php 的时候,很可能是在通过处理 Products.aspx 或者 Products.php 文件来完成任务. ASP.NET MVC 的处理方式则不同,它没有映射到文件上,相反,将这些 URL 地址映射到类

ASP.NET MVC 音乐商店 - 10. 完成导航和站点的设计

转自 http://www.cnblogs.com/haogj/archive/2011/11/20/2255680.html 我们已经完成了网站的大部分工作,但是,还有一些添加到站点的导航功能,主页,以及商店的浏览页面. 创建购物车汇总部分视图 我们希望在整个站点的页面上都可以看到购物车中的数量.                         通过创建一个部分视图,然后添加到网站的布局中就可以容易地完成, 前面看到,在 ShoppingCart 控制器中包含了一个名为 CartSummary

ASP.NET MVC 音乐商店 - 6. 使用 DataAnnotations 进行模型验证

转自http://www.cnblogs.com/haogj/archive/2011/11/16/2251920.html 在前面的创建专辑与编辑专辑的表单中存在一个问题:我们没有进行任何验证.字段的内容可以不输入,或者在价格的字段中输入一些字符,在执行程序的时候,这些错误会导致数据库保存过程中出现错误,我们将会看到来自数据库的错误信息. 通过为模型类增加数据描述的 DataAnnotations ,我们可以容易地为应用程序增加验证的功能.DataAnnotations  允许我们描述希望应用

ASP.NET MVC 音乐商店 - 5. 通过支架创建编辑表单

转自 http://www.cnblogs.com/haogj/archive/2011/11/15/2249143.html 在上一章,我们已经从数据库获取数据,然后显示出来,这一章,我们将允许编辑数据.  创建 StoreManagerController 控制器 我们将要创建称为 StoreManager 的控制器,对于这个控制器,我们将通过使用 ASP.NET MVC3 中提供的脚手架功能来实现.在添加控制器的窗口中,注意需要选中 为"创建"."更新".&q

ASP.NET MVC 音乐商店 - 5 通过支架创建编辑表单 续

转自http://www.cnblogs.com/haogj/archive/2011/11/15/2249147.html 查看 StoreManager 控制器的代码 现在,Store Manager 控制器中已经包含了一定数量的代码,我们从头到尾重新过一下. 首先,在控制器中包含了标准的 MVC 控制器的代码,为了使用方便,还可以引用我们的模型类所在的命名空间 MvcMusicStore.Models.控制器还拥有了一个私有的 MusicStoreEntities 的私有成员,以方便控制器

ASP.NET MVC 音乐商店简单应用实例

我们的项目从在 Visual Studio 中的文件菜单中选择"新建",选择"项目"开始.   然后,选择 C# 中的 Web 模板组,在右边的项目模板中选择 ASP.NET MVC3 Web 应用程序,在项目的名称输入框中,输入 MvcMusicStore ,点击确定.   这时,你会到第二个对话框,允许我们设置这个项目关于 MVC 的一些设置,确认选中了"空"项目模板,视图引擎选中 Razor ,点击确定.   这样我们的项目就创建成功了!

ASP.NET MVC 音乐商店 - 7.成员管理和授权

转自http://www.cnblogs.com/haogj/archive/2011/11/18/2253140.html   目前,我们的 Store Manager 可以被任何人访问,让我们限制一下对站点管理的访问. 增加 AccountController 和 相应的视图 在全功能的 ASP.NET MVC3 Wb 应用程序与空的 ASP.NET MVC3 应用程序模板之间的区别在于,空的应用程序模板中没有包含账号控制器,我们可以从新创建的全功能的 ASP.NET MVC 应用程序中复制