《Pro ASP.NET MVC 3 Framework》学习笔记之十六【示例项目SportsStore】

提交订单:这是SportsStore项目的最后一个功能了,结算并完成我们的订单。

在Entities文件夹里添加一个ShippingDetails.cs,代码如下:

View Code

    public class ShippingDetails    {        [Required(ErrorMessage = "Please enter a name")]public string Name { get; set; }        [Required(ErrorMessage = "Please enter the first address line")]public string Line1 { get; set; }public string Line2 { get; set; }public string Line3 { get; set; }        [Required(ErrorMessage = "Please enter a city name")]public string City { get; set; }        [Required(ErrorMessage = "Please enter a state name")]public string State { get; set; }public string Zip { get; set; }        [Required(ErrorMessage = "Please enter a country name")]public string Country { get; set; }public bool GiftWrap { get; set; }    }

注意这里给属性添加了一些validation attributes,需要引入一个命名空间:System.ComponentModel.DataAnnotations。

添加结算处理:我们的目标是能够让用户添加快递信息和提交订单。首先我们在购物车详情页面添加一个Checkout按钮,修改Views/Cart/Index.cshtml如下:

<p align="center" class="actionButtons">    <a href="@Model.ReturnUrl">Continue shopping</a>@Html.ActionLink("Checkout now", "Checkout")</p>

接着我们在CartController里面添加一个Checkout action方法,如下:

        public ViewResult Checkout()        {return View(new ShippingDetails());        }

右键添加一个视图Checkout,如下:

Checkout.cshtml的代码如下:

View Code

@model SportsStore.Domain.Entities.ShippingDetails@{    ViewBag.Title = "SportsStore:Checkout";}<h2>    Check out now</h2>Please enter your details,and we'll ship your goods right way!@using (Html.BeginForm()){    @Html.ValidationSummary()    <h3>        Ship to</h3>    <div>        Name:@Html.EditorFor(x => x.Name)</div>    <h3>        Address</h3>    <div>        Line 1:@Html.EditorFor(x => x.Line1)</div>     <div>        Line 2:@Html.EditorFor(x => x.Line2)</div>    <div>        Line 3:@Html.EditorFor(x => x.Line3)</div>    <div>        City:@Html.EditorFor(x => x.City)</div>    <div>        State:@Html.EditorFor(x => x.State)</div>    <div>        Zip:@Html.EditorFor(x => x.Zip)</div>    <div>        Country:@Html.EditorFor(x => x.Country)</div>

<h3>        Options</h3>    <label>        @Html.EditorFor(x => x.GiftWrap)        Gift wrap these items    </label>

<p align="center">        <input class="actionButtons" type="submit" value="Complete order" />    </p>}

我们使用Html.EditorFor辅助方法为每一个表单字段呈现input元素,我们让MVC框架能够算出view model属性需要哪一种input元素,而不是显示的指定。Html.EditorFor方法是Template View Helper一个例子,书后面的章节会进行详细的讲解。这里我们能体验到,MVC框架非常智能,会自动为bool类型的属性呈现一个Checkbox(这里的Gift Wrap).

Tip:我们可以使用一个简单的方法Html.EditorForModel来为所有的属性创建HTML元素.将上面的代码注释,然后加上@Html.EditorForModel().运行程序可以看看效果。我们这是分开写的,为的是能够直接引用每一个属性。

接着实现订单处理过程

我们需要一个组件来处理订单的详情,为了保持MVC model的严则,首先定义一个接口,并实现该接口。然后使用我们的DI容器--Ninject
在 SportsStore.Domain/Abstract的文件夹里面定义一个IOrderProcessor接口,如下所示:

View Code

namespace SportsStore.Domain.Abstract{public interface IOrderProcessor    {void ProcessOrder(Cart cart, ShippingDetails shippingDetails);    }}

接着实现该接口,这里比较简单的处理,仅仅发一封邮件告之。在 SportsStore.Domain/Concrete里面创建一个EmailOrderProcessor类来实现该接口,如下所示:

View Code

using System.Net.Mail;using System.Text;using SportsStore.Domain.Abstract;using SportsStore.Domain.Entities;using System.Net;

namespace SportsStore.Domain.Concrete{public class EmailSettings    {public string MailToAddress = "**@**";public string MailFromAddress = "mszhangxuefei@qq.com";public bool UseSsl = false;public string UserName = "mszhangxuefei@qq.com";public string Password = "这个不能说";public string ServerName = "smtp.qq.com";public int ServerPort = 25;public bool WriteAsFile = false;public string FileLocation = @"D:\Study\Projects\SportsStore\sports_store_emails";

}

public class EmailOrderProcessor : IOrderProcessor    {private EmailSettings emailSettings;public EmailOrderProcessor(EmailSettings settings)        {            emailSettings = settings;        }public void ProcessOrder(Cart cart, ShippingDetails shippingDetails)        {using (var smtpClient = new SmtpClient())            {                smtpClient.EnableSsl = emailSettings.UseSsl;                smtpClient.Host = emailSettings.ServerName;                smtpClient.Port = emailSettings.ServerPort;                smtpClient.UseDefaultCredentials = false;                smtpClient.Credentials = new NetworkCredential(emailSettings.UserName, emailSettings.Password);

if (emailSettings.WriteAsFile)                {//如果你没有可用的smtp服务器,则将邮件复制到指定目录                    smtpClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;                    smtpClient.PickupDirectoryLocation = emailSettings.FileLocation;                    smtpClient.EnableSsl = false;                }

StringBuilder body = new StringBuilder()                       .AppendLine("A new order has been submitted")                       .AppendLine("---")                       .AppendLine("Items:");

foreach (var line in cart.Lines)                {var subtotal = line.Product.Price * line.Quantity;                    body.AppendFormat("{0}*{1} (subtotal:{2:c}", line.Quantity, line.Product.Name, subtotal);                }

body.AppendFormat("Total order value:{0:c}", cart.ComputeTotalValue())                    .AppendLine("---")                    .AppendLine("Ship to:")                    .AppendLine(shippingDetails.Name)                    .AppendLine(shippingDetails.Line1)                    .AppendLine(shippingDetails.Line2 ?? "")                    .AppendLine(shippingDetails.Line3 ?? "")                    .AppendLine(shippingDetails.City)                    .AppendLine(shippingDetails.State ?? "")                    .AppendLine(shippingDetails.Country)                    .AppendLine(shippingDetails.Zip)                    .AppendLine("---")                    .AppendFormat("Gift wrap:{0}", shippingDetails.GiftWrap ? "Yes" : "No");

MailMessage mailMessage = new MailMessage(                    emailSettings.MailFromAddress, //From                    emailSettings.MailToAddress, //To                    "New order submitted",//Subject                    body.ToString());//Body

if (emailSettings.WriteAsFile)                {                    mailMessage.BodyEncoding = Encoding.ASCII;                }                smtpClient.Send(mailMessage);            }        }    }}

接着使用Ninject绑定,在NinjectControllerFactory的修改AddBindings()如下:

        private void AddBindings()        {            ninjectKernel.Bind<IProductsRepository>().To<ProductsRepository>();

EmailSettings emailSettings = new EmailSettings {  WriteAsFile = bool.Parse(ConfigurationManager.AppSettings["Email.WriteAsFile"] ?? "false") }; ninjectKernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>().WithConstructorArgument("settings", emailSettings);        }

对了,这里的Email.WriteAsFile在配置文件里面配置的,主要是考虑没有smtp服务器时,将邮件复制到指定目录。其实一般的邮箱都开通了smtp服务的,所以我们将这里的默认值设为false。在Web.config里面配置<add key="Email.WriteAsFile" value="false"/>
在Ninject里面绑定时,这里是带了构造器参数,如果初次路过的朋友不是很清楚,可以看前面的比较,有针对Ninject的介绍。

接着完成CartController

我们需要修改CartController的构造器,现在需要增加IOrderProcessor类型的参数,并且需要增加一个新的action方法来处理当用户点击完成订单按钮后的post请求。修改后如下:

        private IProductsRepository repository;private IOrderProcessor orderProcessor;public CartController(IProductsRepository repo, IOrderProcessor proc)        {            repository = repo;            orderProcessor = proc;        }

[HttpPost]public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails)        {if (cart.Lines.Count() == 0)            {                ModelState.AddModelError("", "Sorry,your cart is empty!");            }if (ModelState.IsValid)            {                orderProcessor.ProcessOrder(cart, shippingDetails);                cart.Clear();return View("Completed");            }else            {return View(shippingDetails);            }        }

这里可以发现有这样一个[HttpPost]特性修饰Checkout方法,表示该方法只有发起post请求时才会被调用。当用户提交表单以后,这里又一次用到了Model Binding系统,分别针对ShippingDetails自动来自http的数据创建参数和Cart使用我们自定义的绑定模型创建参数。如果你使用了单元测试,这个是需要修改CartController的构造器,可以增加一个null参数来使编译通过。

MVC框架通过使用data annotation attributes检查我们对ShippingDetails应用的验证约束,任何的验证都会通过ModelState传递给action方法。我可以通过检查ModelState.IsValid属性来确定验证过程产生的问题,比如是否为空等等。注意到这里,如果没有任何的项在购物车里面,我们能够调用ModelState.AddModelError方法来注册一个错误的信息.关于Model Binding和validation,书中第二部分有章节进行专门的讲解,如果你这里跟我一样不太清楚,没问题的。

展示验证信息

当用户输入了不合法的数据时,我们可以使用@Html.ValidationSummary()在显示,当然这是在一个地方统一显示。你也可以借助其他的方法来分开显示不同的错误信息。比如这修改Checkout.cshtml如下:

... <h2>Check out now</h2> Please enter your details, and we'll ship your goods right away! @using (Html.BeginForm()) { 

@Html.ValidationSummary() 

<h3>Ship to</h3>     <div>Name: @Html.EditorFor(x => x.Name)</div> ... 

展示效果如下:

接下进行最后一步了,当用户填完了Ship信息时,给用户显示一个完结的页面Summary。右键Checkout方法,添加视图Completed,这里不用强类型视图。因为就显示一个成功信息而已。代码如下:

View Code

@model SportsStore.Domain.Entities.Cart@{    Layout = null;}<div id="cart">    <span class="caption"><b>Your cart:</b>        @Model.Lines.Sum(x => x.Quantity) item(s),        @Model.ComputeTotalValue().ToString("c")    </span>    @Html.ActionLink("Checkout", "Index", "Cart", new { returnUrl = Request.Url.PathAndQuery }, null)</div>

好啦,今天的笔记就到这里。

下一章也是关于该项目的,也是第一部分的最后一章了,正在学习mvc的朋友请一起坚持。到了第二部分,就进入mvc详细讲解部分了,我觉得是最核心的部分,我相信在这个项目里面我们所有的疑惑和问题都会在第二部分的内容得到答案。你相信吗?呵呵!

晚安!

时间: 2024-09-24 17:39:49

《Pro ASP.NET MVC 3 Framework》学习笔记之十六【示例项目SportsStore】的相关文章

《Pro ASP.NET MVC 3 Framework》学习笔记之十七【示例项目SportsStore】

接下来是关于SportsStore的后台管理功能,也就是通常的CRUD操作.首先添加一个AdminController,代码如下: View Code using System.Web.Mvc; using SportsStore.Domain.Abstract; namespace SportsStore.WebUI.Controllers { public class AdminController : Controller { private IProductRepository repo

《Pro ASP.NET MVC 3 Framework》学习笔记之十三【示例项目SportsStore】

接着我们添加一个分页功能.修改ProductController,如下所示: public class ProductController : Controller {public int PageSize = 4;//后面会更改 private IProductsRepository repository;public ProductController(IProductsRepository productRepository) { repository = productRepositor

ASP.NET MVC 3 Framework学习笔记之Model Templates

.使用模板化的视图Helpers(Using Templated View Helpers) 模版化视图helpers的创意就是它们更加灵活.我们不用自己去指定应该用什么HTML元素来呈现一个模型的属性,MVC自己会搞定,在我们更新了视图模型时,也不用手动的更新视图.下面是一个例子:  代码如下 复制代码 //在Models里面添加Persons.cs using System; using System.Collections.Generic; using System.Linq; using

《Pro ASP.NET MVC 3 Framework》学习笔记目录

<Pro ASP.NET MVC 3 Framework>简介: 作者: Adam Freeman 和 Steven Sanderson 出版社: Apress; New 平装: 820页 语种: 英语 ISBN: 1430234040 声明:笔记里面按我自己的理解翻译了大部分内容,写这个笔记的目的:为了方便自己查阅,也为园友提供学习的方便. 我无意侵犯作者的任何权利,仅仅为了自己学习.也希望路过的朋友不要用于任何商业目的. 第一部分 ASP.NET MVC3介绍   <Pro ASP.

《Pro ASP.NET MVC 3 Framework》学习笔记之一【MVC的历程,优点,HelloWorld】

序论:asp.net mvc出现已经有两三年的时间了(2009开始1.0版本),但是这么方面的中文学习资料仍然非常少,特别是asp.net mvc3,几乎就没有中文的学习书籍.在英文的书籍中有两本是非常经典的mvc3教程:<Professional ASP.NET MVC 3>--作者:Jon Galloway , Phil Haack, Brad Wilson , K. Scott Allen和<Pro ASP.NET MVC 3 Framework>--作者:Steven Sa

ASP.NET MVC Web API 学习笔记----HttpClient简介

  1. HttpClient简单介绍  依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient,用于接收HttpResponseMessage和发送HttpRequestMesssage. 问题在于既然WebClient,HttpWebRequest可以完成相应的功能,为什么还要使用HttpClient类,.NET Framework中既然提出了这样一个类肯定是有其特别之处的,这里罗列几个不同之处: (

ASP.NET MVC Web API 学习笔记---联系人增删改查

本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系人          说明:为了方便数据不使用真正的数据库,而是通过内存数据模拟    1.       Web API中包含的方法 Action HTTP method Relative URI GetAllContact GET /api/contact GetContact GET /api/

《Pro ASP.NET MVC 3 Framework》学习笔记之九【Ninject的使用-下】

接着上次的Ninject的笔记,如果你是初次路过,可以先看看我前面的笔记. 一,创建依赖链(Chains of Dependency) 当我们向Ninject请求创建一个类型时,Ninject会去检查该类型和其他类型之间的耦合关系.如果有额外的依赖,Ninject也会解析它们并创建我们需要的所有类的实例.为了进一步说明,我们创建一个新的接口和一个实现该接口的类.请注意我们的例子是跟前面的笔记衔接的,所以如果你打算跟着一起操作的话,最好能够去看看前面的笔记. 创建一个IDiscountHelper

《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】

主题:应用领域驱动开发(Applying Domain-Driven Development) Domain Model是MVC程序的"心脏",其他的一切,包括Controllers和Views仅仅是用来跟Domain Model交互的一种方式,ASP.NET MVC并没有限制使用在Domain Model上面的技术,我们可以自由的选择跟.net framework交互的技术,并且这样的选择是非常多的.不仅如此,ASP.NET MVC为我们提供了基础的架构和约定来帮助Domain Mo

《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】

一,创建松耦合的组件 1."分解关注点"是MVC模式里面一个非常重要的特性.我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系.理想情况下,每个组件都是孤立的,不知道其他组件的存在,处理应用程序的其他领域仅仅通过抽象接口,这就是所谓的松耦合,它让我们的应用程序更加容易测试和修改.通过一个简单的例子可以帮助我们理解,假如我们想写一个发邮件的组件,暂且就把这个组件命名为MyEmailSender,接着我们实现一个接口,这个接口定义了所有需要发送邮件的功能,也暂且