• 使用MVC4,Ninject,EF,Moq,构建一个真实的应用电子商务SportsStore(九)


    实在不好意思,好久没有更新了,我不想找些客观原因来解释,只想请大家见谅!现在我们继续我们的项目,客户已经完成了订单的确认,但我们还没有一个地方可以让客户输入他们的收货信息,我们的商品没办法发货,这是个严重的问题,我们必须解决它。现在,我们就在SportsStore.Domain工程的Entities文件夹中添加一个ShippingDetails类,在这个类中,我们使用了System.ComponentModel.DataAnnotations命名空间,去验证客户的输入:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.ComponentModel.DataAnnotations;
    
    namespace SportsStore.Domain.Entities
    {
        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; }
        } 
    }

    我们的目的是让用户输入收货的详细信息后能够去付款,毕竟要赚钱吗,这个没啥不好意思的,我们这就去修改一下我们的summary视图,打开Views/Cart/Index.cshtml文件,我们要在这添加一个支付按钮,修改文件的最后部分像下面的样子,然后,运行一下你的代码,看看效果:)

    </table> <p align="center" class="actionButtons">
             <a href="@Model.ReturnUrl">继续购物</a>
             @Html.ActionLink("支付", "Checkout") </p>

    正如你所预见的,现在我们要为CartController类添加一个Checkout Action方法:

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

    Checkout方法返回一个默认的view,并传递一个new ShippingDetails对象作为view model. 现在我们就去创建一个ShippingDetails类型的强视图:

    image

    修改视图代码如下:

    @model SportsStore.Domain.Entities.ShippingDetails  
    @{     ViewBag.Title = "SportStore: Checkout"; }  
    <h2>现在支付</h2> 
    请输入你的详细信息, 我们会根据您的信息发货! 
    @using (Html.BeginForm()) {    
                                                                                                
                               <h3>发货到</h3>     
        <div>姓名: @Html.EditorFor(x => x.Name)</div>  
        <h3>地址</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>城市: @Html.EditorFor(x => x.City)</div>    
         <div>区: @Html.EditorFor(x => x.State)</div>   
          <div>邮编: @Html.EditorFor(x => x.Zip)</div>    
         <div>国家: @Html.EditorFor(x => x.Country)</div>  
        <h3>可选项</h3>    
         <label>         
             @Html.EditorFor(x => x.GiftWrap)         作为礼品包装我的商品    
    
         </label>             
         <p align="center">        
              <input class="actionButtons" type="submit" value="完成订单" />    
    
         </p>
    }

    实现订单处理器

    我们需要一个组件,通过这个组件,我们能够容易的把握订单的处理流程,为了保持遵守MVC模型的基本原则,我们要定义一个接口,写一个这个接口的实现类,使我们的DI容器和 Ninject和这个实现类整合在一起.
    添加一个IOrderProcessor接口到SportsStore.Domain工程的Abstract文件夹:

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

    实现接口

    IOrderProcessor接口的实现类将通过发送额email给管理员处理订单,当然了,我们简化了这个流程,真正的大型商务网站不只是发邮件这么简单! 现在我们要创建一个新类,叫做 EmailOrderProcessor,把它放在SportsStore.Domain工程的Concrete文件夹中,这个类我们使用.NET Framework library内建的SMTP去发送邮件:

    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 = "orders@example.com";    
            public string MailFromAddress = "sportsstore@example.com";  
            public bool UseSsl = true;   
            public string Username = "MySmtpUsername";   
            public string Password = "MySmtpPassword";    
            public string ServerName = "smtp.example.com"; 
            public int ServerPort = 587;      
            public bool WriteAsFile = false;   
            public string FileLocation = @"c:sports_store_emails";   
        }  
    
        public class EmailOrderProcessor :IOrderProcessor {   
      
            private EmailSettings emailSettings;  
    
            public EmailOrderProcessor(EmailSettings settings) 
            { 
                emailSettings = settings;      
            }  
    
            public void ProcessOrder(Cart cart, ShippingDetails shippingInfo) 
            { 
     
                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) {     
                        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} x {1} (subtotal: {2:c}", line.Quantity, line.Product.Name, subtotal);                 }  
                    body.AppendFormat("Total order value: {0:c}", cart.ComputeTotalValue()).AppendLine("---")
                        .AppendLine("Ship to:").AppendLine(shippingInfo.Name).AppendLine(shippingInfo.Line1)
                        .AppendLine(shippingInfo.Line2 ?? "") .AppendLine(shippingInfo.Line3 ?? "")
                        .AppendLine(shippingInfo.City).AppendLine(shippingInfo.State ?? "")
                        .AppendLine(shippingInfo.Country) .AppendLine(shippingInfo.Zip).AppendLine("---")
                        .AppendFormat("Gift wrap: {0}",shippingInfo.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能够创建 IOrderProcessor接口的实现类,我们必须添加一些代码到SportsStore.WebUI 工程的NinjectControllerFactory 类的AddBindings 方法,到这个方法里添加代码,不用我说你也知道要干什么事了,马上动手吧!

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using SportsStore.Domain.Abstract;
    using SportsStore.Domain.Entities;
    using Moq;
    using Ninject;
    using SportsStore.Domain.Concrete;
    using System.Configuration; 
    
    namespace SportsStore.WebUI.Infrastructure
    {
        public class NinjectControllerFactory: DefaultControllerFactory
        {
    
                private IKernel ninjectKernel;
    
                public NinjectControllerFactory() {
                    ninjectKernel = new StandardKernel();
                    AddBindings();
                }
    
                protected override IController GetControllerInstance(RequestContext
                    requestContext, Type controllerType) {
    
                    return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
                }
    
                private void AddBindings() {
    
                    Mock<IProductsRepository> mock = new Mock<IProductsRepository>();
    
                    //mock.Setup(m => m.Products).Returns(new List<Product> {
                    //    new Product { Name = "Football", Price = 25 },
                    //    new Product { Name = "Surf board", Price = 179 },
                    //    new Product { Name = "Running shoes", Price = 95 }
                    //}.AsQueryable());
                    //ninjectKernel.Bind<IProductsRepository>().ToConstant(mock.Object);
                    ninjectKernel.Bind<IProductsRepository>().To<EFProductRepository>();
    
                    EmailSettings emailSettings = new EmailSettings {
                        WriteAsFile = bool.Parse(ConfigurationManager.AppSettings["Email.WriteAsFile"] ?? "false") };
                    ninjectKernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>().WithConstructorArgument("settings", emailSettings); 
                }
             }
        }

    我们创建了一个EmailSettings 对象, 当一个服务请求要求创建一个新的IOrderProcessor 接口实力的时候,我们使用Ninject的WithConstructorArgument 方法注入它到EmailOrderProcessor构造函数,因为我们使用了ConfigurationManager.AppSettings 属性去访问 Web.config文件,所以,我们要将一些配置添加到Web.config 文件中:

    <appSettings>
        <add key="webpages:Version" value="2.0.0.0" />
        <add key="webpages:Enabled" value="false" />
        <add key="PreserveLoginUrl" value="true" />
        <add key="ClientValidationEnabled" value="true" />
        <add key="UnobtrusiveJavaScriptEnabled" value="true" />
        <add key="Email.WriteAsFile" value="true"/>
      </appSettings>

    完善Cart Controller
    我们要修改CartController类,让它的构造函数去要求一个IOrderProcessor接口的实现,并添加一个新的方法处理用户点击完成订单按钮时,post过来的Http请求:

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

    你现在看到了,我们添加的Checkout方法带有一个HttpPost 属性,这意味着它将为一个post请求调用,当用户提交一个表单时,我们将依赖MVC的model binder system, ShippingDetails 参数和Cart 参数创建我们的model binder。

    为了展示用户的输入错误,我们需要在Checkout view中添加 @Html.ValidationSummary() 标记,看起来应该像下面的样子:

    <h2>现在支付</h2> 
    请输入你的详细信息, 我们会根据您的信息发货! 
    @using (Html.BeginForm()) {   
         @Html.ValidationSummary()  
         <h3>发货到</h3>
         ……

    展示Summary页
    为了完善支付流程, 我们应该显示一个订单已经被处理的确认页给用户,右击CartController类的任意方法去添加一个视图,命名为Completed,这个视图我们不需要定义为强类型:

    image

    @{     ViewBag.Title = "SportsStore: Order Submitted"; }  
    <h2>谢谢!</h2>
     感谢您购买我们的商品. 我们将尽可能快的发送货物给您.

    运行你的程序前,别忘了修改你的email账户和密码,还有C盘下要建一个c:sports_store_emails文件夹哦!在下一篇中,我们将为我们的网站创建一个CRUD的管理后台,这是所有网站都比不可少的功能,我们当然也不会少了!感谢您的关注!如果有任何问题请在我的博客上留言,我会尽可能详尽的为您解答,下篇再见!

    image

  • 相关阅读:
    8月工作杂记
    好用的MarkDown编辑器
    Windows下遍历某目录下的文件
    Visual Assist 试用期过期怎么办?
    网易有道面试
    Windows操作系统C盘占用空间过多
    如果有一天我当了面试官
    matlab进行三维重建
    HBase , Doris
    《Java程序设计》第6周学习总结
  • 原文地址:https://www.cnblogs.com/bzwang/p/3139553.html
Copyright © 2020-2023  润新知