MVC編程實例----簡易電子商務網站(一)

一.整體概覽、規劃

  本文將會建立一個基本的電子商務網站。因爲電子商務網站的基本功能都是差很少的,此處省去了需求分析等工做,直接總結出結論。分爲4個基本功能:html

  • 商品瀏覽
  • 會員功能
  • 購物車
  • 訂單結帳

  其中每項還能夠細分,因爲比較簡單再也不贅述。jquery

二.實現思路和步驟

  因爲項目是很是簡單的,這裏並無採用分層結構。MVC分別表明了三個部分,Model、View和Control。開發過程當中創立對象時誰先誰後?在《ASP.NET MVC4開發指南》中(本文就是採用了該書中的實例),做者黃保翕這樣闡述「以筆者的實務開發經驗來講,以爲M(Model)是MVC架構的中心,有了Model以後就可讓Controller與View參考這些Model(模型),先定義出計劃開發的Controller與Action,而後再建立全部Action對應的View(無屬性的View),以後就能夠將不一樣單元的Controller與View分工開發,最後再進行集成便可」,本項目的實現步驟就基本是按照這樣的思想。首先按照功能分爲4個部分(這樣進一步簡化了問題),每一個功能的實現經過三步來完成,最後把他們集成到一塊兒。ajax

  另外,雖然原來的範例中使用了code first技術,我等菜鳥仍是習慣從db first,對localdb不甚瞭解,這個範例仍是先建了數據庫MVCShopping,並錄入了一些測試數據。數據庫

三.按功能具體實現

  依次實現各個部分:網絡

1.商品瀏覽

  這是最簡單通用的一部分,與經典的MVC Movie Store中所呈現的業務邏輯同樣。包括:商品類別列表、某類別下的商品列表和某一商品的詳細信息。按照實現步驟,分三步實現:session

1)數據模型規劃

  數據模型涉及到了兩個數據實體,商品類別(ProductCategory)和商品信息(Product)。在Models文件夾下新建兩個類:ProductCategory和Product架構

 [DisplayName("商品類別")]
    [DisplayColumn("Name")]
    public class ProductCategory
    {
        [Key]
        public int Id { get; set; }

        [DisplayName("商品類別名稱")]
        [Required(ErrorMessage="請輸入商品類別名稱")]
        [MaxLength(20,ErrorMessage="類別名稱不可超過20個字")]
        public string Name { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }
 [DisplayName("商品信息")]
    [DisplayColumn("Name")]
    public class Product
    {
        [Key]
        public int Id { get; set; }

        [DisplayName("商品類別")]
        [Required]
        public virtual ProductCategory ProductCategory { get; set; }

        [DisplayName("商品名稱")]
        [Required(ErrorMessage = "請輸入商品名稱")]
        [MaxLength(60, ErrorMessage = "不得超過60個字")]
        public string Name { get; set; }

        [DisplayName("商品簡介")]
        [Required(ErrorMessage = "請輸入商品簡介")]
        [MaxLength(250, ErrorMessage = "商品簡介不超過250字")]
        public string Description { get; set; }

        [DisplayName("商品顏色")]
        [Required(ErrorMessage = "請選擇顏色")]
        public Color Color { get; set; }

        [DisplayName("商品售價")]
        [Required(ErrorMessage = "請輸入商品售價")]
        [Range(1,10000,ErrorMessage="商品售價必須介於1到10000之間")]
        public int Price { get; set; }

        [DisplayName("上架時間")]
        [Description("若是不設置上架時間,表明不發售")]
        public DateTime? PublishOn{get;set;}
    }

  在這裏可能新手會比較困惑的是每一個字段上都有形如[DisplayName("商品名稱")] [Required]等內容。這些統稱爲註解,可分爲驗證註解和顯示註解兩類。後面會見到不少諸如此類的註解,便於方便和驗證。mvc

2)控制器架構規劃

  瀏覽商品主要會涉及到三個頁面:「首頁」顯示商品類別列表,「列表頁」顯示商品列表,「詳細頁」顯示商品明細。把這三個也的功能放在一個Controller控制器中。在Controllers文件夾中添加控制器,HomeController。測試

public class HomeController : Controller
    {
        MvcShoppingContext db = new MvcShoppingContext();
        // 首頁
        public ActionResult Index()
        {
            var data = db.ProductCategories.ToList();
            data = db.ProductCategories.ToList();
            return View(data);
        }
        //商品列表
        public ActionResult ProductList(int id)
        {
            var productCategory = db.ProductCategories.Find(id);
            var data = productCategory.Products.ToList();
            return View(data);
        }
        //商品明細
        public ActionResult ProductDetail(int id)
        {
            var data = db.Products.Find(id);
            return View(data);

        }
    }
  很簡單,根據類別id查找某一類別商品,根據商品id查找商品詳細信息。
  惟一可能產生疑問的在於 return View(data)這句代碼。在vs中查看View方法的重載,發現它既能夠傳string類型的ViewName值來指定具體視圖頁面,也能夠傳object類型的model值來傳遞數據。實際上,經過View(data)把數據傳給視圖對象時,就等價因而:ViewData.Model=data;而咱們知道視圖經過強類型的參數能夠方便不少操做,因此此處其實是進行了簡化,直接把data經過View()方法傳過來而後 在視圖中用@model 指定類型進行強類型的轉換。操做更加簡潔。
3)建立視圖頁面

  最後建立展現頁面。在HomeController控制器中添加相應的視圖。網站

Index.cshtml

@model IEnumerable<MVCShopping.Models.ProductCategory>

<h2>@Html.DisplayNameFor(t=>t.Name)</h2>
<ul>
    @foreach (var item in Model)
    { 
      <li>@Html.ActionLink(item.Name, "ProductList", new { id=item.Id})</li>
    }
</ul>

ProductList.cshtml

@model IEnumerable<MVCShopping.Models.Product>
@{
    var ajaxOption = new AjaxOptions() {
        HttpMethod="Post",
        OnSuccess = "AddToCartSuccess",
        OnFailure = "AddToCartFailure",
    };
}
@section scripts{
@Scripts.Render("~/bundles/jqueryval")
    <script>
        function AddToCartSuccess() {
            alert('添加購物從成功');
        }
        function AddToCartFailure(xhr) {
            alert('添加購物車失敗(HTTP狀態代碼:'+xhr.status+')');
        }
    </script>

}
<h2>@Html.DisplayNameFor(t => t.ToList()[0])</h2>
<h2>@Html.DisplayNameFor(t => t.Name)</h2>
<h3>您正在瀏覽[@Model.First().ProductCategory.Name]分類的信息</h3>

<table>
    <tr>
        <th>@Html.DisplayNameFor(model=>model.Name)</th>
        <th>@Html.DisplayNameFor(model => model.Description)</th>
        <th>@Html.DisplayNameFor(model => model.Price)</th>
        <th>添加購物車</th>
    </tr>
    @foreach (var item in Model)
    { 
        <tr>
            <td>@Html.ActionLink(item.Name, "ProductDetail", new { id=item.Id})</td>
            <td>@Html.DisplayFor(w=>item.Description)</td>
            <td>@Html.DisplayFor(w=>item.Price)</td>
            <td>@Ajax.ActionLink("添加購物車", "AddToCart", "Cart", new { ProductId=item.Id},ajaxOption)</td>
        </tr>
    }
</table>

ProductDetail.cshtml

@model MVCShopping.Models.Product

@{
    var ajaxOption = new AjaxOptions()
    {
        OnSuccess = "AddToCartSuccess",
        OnFailure = "AddToCartFailure",
    };
}
@section scripts{
    @Scripts.Render("~/bundles/jqueryval")
    <script>
        function AddToCartSuccess() {
            alert('添加成功');
        }
        function AddToCartFailure(xhr) {
            alert('添加購物車失敗(HTTP狀態代碼:' + xhr.status + ')');
        }
    </script>
    }

<h2>您正在察看"@Model.Name"商品</h2>
<fieldset>
    <legend>@Html.DisplayNameFor(m=>m)</legend>
    <div class="display-label">
        @Html.DisplayNameFor(model=>model.Description)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Description)
    </div>

    <div class="display-label">
        @Html.DisplayNameFor(model => model.Price)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Price)
    </div>

    <div class="display-label">
        @Html.DisplayNameFor(model => model.PublishOn)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.PublishOn)
    </div>
</fieldset>
<p>
    @Ajax.ActionLink("添加購物車","AddToCart","Cart",ajaxOption)
</p>

  能夠看出來,不管是html輔助方法仍是ajax輔助方法的使用都是很固定很簡潔的,也比較容易掌握,要否則也不會mvc能根據模板自動生成許多html控件。這裏的頁面使用了許多強類型的輔助方法,多寫幾個頁面就覺着都是套路。比我菜和跟我同樣菜的能夠看我總結的這些內容,基本都涵蓋了。

2.會員功能

  這裏的會員功能主要用到了經常使用的Forms認證。登陸和註銷的方法分別是:FormsAuthentication.SetAuthCookie()和FormsAuthentication.SignOut()。詳見Fish Li大神的這篇文章。仍是分三步走。

1)數據模型規劃

Member.cs

[DisplayName("會員信息")]
    [DisplayColumn("Name")]
    public class Member
    {
        [Key]
        public int id { get; set; }


        [DisplayName("會員賬號")]
        [Description("以Email爲會員的登錄賬號")]
        [Required(ErrorMessage = "請輸入Email地址")]
        [MaxLength(250, ErrorMessage = "不得超過250個字")]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }

        [DisplayName("會員密碼")]
        [Description("密碼將以SHA1進行哈西運算,經過運算後的結果轉爲HEX表示法的字符串長度都爲40")]
        [Required(ErrorMessage = "請輸入密碼")]
        [MaxLength(40, ErrorMessage = "不得超過40個字")]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [DisplayName("中文姓名")]
        [Description("忽略外國人")]
        [Required(ErrorMessage = "請輸入中文姓名")]
        [MaxLength(5, ErrorMessage = "不得超過5個字")]
        public string Name { get; set; }

        [DisplayName("網絡暱稱")]
        [Required(ErrorMessage = "請輸入網絡暱稱")]
        [MaxLength(15, ErrorMessage = "不得超過15個字")]
        public string Nickname { get; set; }

        [DisplayName("會員註冊時間")]
        public DateTime RegisterOn { get; set; }

        //AuthCode會保存一個GUID值
        [DisplayName("會員啓用認證碼")]
        [MaxLength(36)]
        [Description("當AuthCode等於null表明會員已經過Email認證")]
        public string AuthCode { get; set; }

        public virtual ICollection<OrderHeader> orders { get; set; }
    }

其中的字段AuthCode是爲了能夠進行會員驗證功能的。當你註冊一個帳號時經常能收到一封確認郵件,這個字段就是爲了實現此功能的。在後面的功能擴展中會進行展現。

MemberLoginViewModel.cs

public class MemberLoginViewModel
    {
        /// <summary>
        /// 在賬戶這顯示指定了DataType(DataType.EmailAddress,ErrorMessage="請輸入您的Email地址")
        /// 可是並不現實錯誤信息,這是由於MVC4並無針對DataType屬性支持客戶端的js驗證功能
        /// </summary>
        [DisplayName("會員賬號")]
        [Required(ErrorMessage = "請輸入{0}")]
        [DataType(DataType.EmailAddress,ErrorMessage="請輸入您的Email地址")]
        public string Email { get; set; }

        [DisplayName("會員密碼")]
        [Required(ErrorMessage = "請輸入{0}")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }

這個Model是爲登陸界面提供的。因爲登陸時只有兩個能夠用到的字段,爲了可以使用強類型方式使用Model,全部新建了一個ViewModel。

2)控制器架構規劃

public class MemberController : Controller
    {
        MvcShoppingContext db = new MvcShoppingContext();
        private string pwSalt = "AlrySq1oPe2Mh784QQwG6jRAfkdPpDa90J0i";
        // 會員註冊頁面

        public ActionResult Register()
        {
            return View();
        }

        //寫入會員信息
        [HttpPost]
        public ActionResult Register([Bind(Exclude="RegisterOn,AuthCode")]Member member)
        {
            //檢查會員是否存在
            var chk_member = db.Members.Where(p => p.Email == member.Email).FirstOrDefault();
            if (chk_member != null)
            {
                ModelState.AddModelError("Email","您輸入的Email已經有人註冊過了!");
            }
            if (ModelState.IsValid)
            { 
                //將密碼加鹽在以後進行哈希運算
                member.Password = FormsAuthentication.HashPasswordForStoringInConfigFile(pwSalt + member.Password, "SHA1");
                member.RegisterOn = DateTime.Now;
                //會員驗證碼,採用Guid當成驗證碼屬性,避免有會員使用到重複的驗證碼
                member.AuthCode = Guid.NewGuid().ToString();
                db.Members.Add(member);
                db.SaveChanges();

                return RedirectToAction("Index","Home");
            }
            else
            { return View(); }
        }

        //顯示會員登錄頁面
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }
        //會員登錄
        [HttpPost]
        public ActionResult Login(string email, string password, string returnUrl)
        {
            if (ValidateUser(email, password))
            {
                FormsAuthentication.SetAuthCookie(email,false);
                if (string.IsNullOrEmpty(returnUrl))
                {
                    return RedirectToAction("Index", "Home");
                }
                else
                    return Redirect(returnUrl);
            }
            ModelState.AddModelError("", "輸入的賬號或者密碼錯誤");
            return View();
        }

        private bool ValidateUser(string email, string password)
        {
            var hash_pw = FormsAuthentication.HashPasswordForStoringInConfigFile(pwSalt + password, "SHA1");
            var member = db.Members.Where(p => p.Email == email && p.Password == hash_pw).FirstOrDefault();
            return(member!=null);
        }

        //會員註銷
        public ActionResult Logout()
        {
            FormsAuthentication.SignOut();
            Session.Clear();
            return RedirectToAction("Index", "Home");
        }


        public ActionResult ValidateRegister()
        {

            return View();
        }
    }

  帶有[HttpPost]標記的代表是Post請求時執行的部分。默認是[HttpGet]。會員功能主要就是包含了註冊和登陸兩部分。

3)建立視圖頁面

Login.cshtml

@model MVCShopping.Models.MemberLoginViewModel

@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>MemberLoginViewModel</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Email)
        </div>
        <div class="editor-field">
            @Html.TextBoxFor(model => model.Email, new { data_val_Email="請輸入Email地址"})
            @Html.ValidationMessageFor(model => model.Email)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Password)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Password)
            @Html.ValidationMessageFor(model => model.Password)
        </div>

        <p>
            <input type="submit" value="登錄" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Register.cshtml

@model MVCShopping.Models.Member

@{
    ViewBag.Title = "註冊";
}

<h2>會員註冊</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>請輸入會員註冊信息</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Email)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Email)
            @Html.ValidationMessageFor(model => model.Email)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Password)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Password)
            @Html.ValidationMessageFor(model => model.Password)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nickname)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nickname)
            @Html.ValidationMessageFor(model => model.Nickname)
        </div>

        <p>
            <input type="submit" value="註冊" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

3.購物車功能

  這個功能主要是用到了Session(我見過的購物車都是以session實現的),在Session中存儲Cart信息。基本上就是圍繞如下這一段關鍵代碼進行的:

List<Cart> Carts
        {
            get {
                if (Session["Carts"] == null)
                {
                    Session["Carts"] = new List<Cart>();
                }
                return Session["Carts"] as List<Cart>;
            }
            set { Session["Carts"] = value; }
        }

1)數據模型規劃

public class Cart
    {
        [DisplayName("選購商品")]
        [Required]
        public Product Product { get; set; }

        [DisplayName("選購數量~")]
        [Required]
        public int Amount { get; set; }
    }

2)控制器架構規劃

public class CartController : Controller
    {
        //非會員也可以使用因此購物車保存在Session中
        // 顯示當前購物從項目
        MvcShoppingContext db = new MvcShoppingContext();
        List<Cart> Carts
        {
            get {
                if (Session["Carts"] == null)
                {
                    Session["Carts"] = new List<Cart>();
                }
                return Session["Carts"] as List<Cart>;
            }
            set { Session["Carts"] = value; }
        }
        public ActionResult Index()
        {
            return View(this.Carts);
        }
        //添加產品項目到購物車,若是沒有傳入Amount參數則默認購買數量爲1
        //由於要經過Ajax調用這個Action,因此能夠先標示Post屬性
        [HttpPost]
        public ActionResult AddToCart(int ProductId, int Amount = 1)
        {
            var product = db.Products.Find(ProductId);
            //驗證產品是否存在
            if (product == null)
                return HttpNotFound();
            var existiongCart = this.Carts.FirstOrDefault(p => p.Product.Id == ProductId);
            if (existiongCart != null)
            {
                existiongCart.Amount += 1;
            }
            else
            {
                this.Carts.Add(new Cart() { Product=product,Amount=Amount});
            }
            return new HttpStatusCodeResult(HttpStatusCode.Created);
        }
        //移出購物從項目
        [HttpPost]
        public ActionResult Remove(int ProductId)
        {
            var existingCart = this.Carts.FirstOrDefault(p => p.Product.Id == ProductId);
            if (existingCart != null)
            {
                this.Carts.Remove(existingCart);
            }
            return new HttpStatusCodeResult(System.Net.HttpStatusCode.OK);
        }
        //更新數量
        [HttpPost]
        public ActionResult UpdateAmount(List<Cart> Carts)
        {
            foreach (var item in Carts)
            {
                var existingCart = this.Carts.FirstOrDefault(p => p.Product.Id == item.Product.Id);
                if (existingCart != null)
                {
                    existingCart.Amount = item.Amount;
                }
            }
            return RedirectToAction("Index","Cart");
        }
    }

3)建立視圖頁面

index.cshtml

@model IEnumerable<MVCShopping.Models.Cart>

@{
    var ajaxOption = new AjaxOptions()
    {
        OnSuccess = "RemoveCartSuccess",
        OnFailure = "RemoveCartFailure",
        Confirm = "您肯定要從購物車刪除嗎?",
        HttpMethod = "Post",
    };
}

@section scripts{
    @Scripts.Render("~/bundles/jqueryval")
    
    <script>
        function RemoveCartSuccess() {
            alert('移出購物從成功');
            location.reload();
        }
        function RemoveCartFailure(xhr)
        {
            alert('移出購物車失敗(Http狀態代嗎:' + xhr.status);
        }
    </script>
    }

<h2>購物車列表</h2>
@using (Html.BeginForm("UpdateAmount", "Cart"))
{ 
    <table>
        <tr>
            <th>產品名稱</th>
            <th>單價</th>
            <th>數量</th>
            <th>小計</th>
            <th></th>
        </tr>
        @{int subTotal = 0;}

        @foreach (var item in Model)
        {
            subTotal += item.Product.Price * item.Amount;
            var ddlAmountList = new SelectList(Enumerable.Range(1,10),item.Amount);
            @Html.Hidden(item.Product.Id.ToString())
            <tr>
                <td>@Html.DisplayFor(t=>item.Product.Name)</td>
                <td>NT¥@(item.Product.Price)</td>
                <td>@Html.DropDownListFor(t=>item.Amount,ddlAmountList)</td>
                <td>NT¥@(item.Product.Price*item.Amount)</td>
                <td>
                    @Ajax.ActionLink("刪除","Remove",new{ProductId=item.Product.Id},ajaxOption)
                </td>
            </tr>
        }
        <tr>
            <th></th>
            <th></th>
            <th>總價</th>
            <th id="subtotal">NT¥ @subTotal</th>
            <th></th>
        </tr>
    </table>
    <p>
        <input type="submit" value="更新數量" />
        <input type="button" value="完成訂單" onclick="location.href='@Url.Action("Complete","Order")';" />
    </p>
}
相關文章
相關標籤/搜索