本文將會建立一個基本的電子商務網站。因爲電子商務網站的基本功能都是差很少的,此處省去了需求分析等工做,直接總結出結論。分爲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,並錄入了一些測試數據。數據庫
依次實現各個部分:網絡
這是最簡單通用的一部分,與經典的MVC Movie Store中所呈現的業務邏輯同樣。包括:商品類別列表、某類別下的商品列表和某一商品的詳細信息。按照實現步驟,分三步實現:session
數據模型涉及到了兩個數據實體,商品類別(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
瀏覽商品主要會涉及到三個頁面:「首頁」顯示商品類別列表,「列表頁」顯示商品列表,「詳細頁」顯示商品明細。把這三個也的功能放在一個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); } }
最後建立展現頁面。在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>
@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>
@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>
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") }
@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") }
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> }