一.总体概览、规划

  本文将会创建一个基本的电子商务网站。由于电子商务网站的基本功能都是差不多的,此处省去了需求分析等工作,直接总结出结论。分为4个基本功能:

  • 商品浏览
  • 会员功能
  • 购物车
  • 订单结账

  其中每项还可以细分,由于比较简单不再赘述。

二.实现思路和步骤

  由于项目是非常简单的,这里并没有采用分层结构。MVC分别代表了三个部分,Model、View和Control。开发过程中创立对象时谁先谁后?在《ASP.NET MVC4开发指南》中(本文就是采用了该书中的实例),作者黄保翕这样阐述“以笔者的实务开发经验来说,觉得M(Model)是MVC架构的中心,有了Model之后就可以让Controller与View参考这些Model(模型),先定义出计划开发的Controller与Action,然后再创建所有Action对应的View(无属性的View),之后就可以将不同单元的Controller与View分工开发,最后再进行集成即可”,本项目的实现步骤就基本是按照这样的思想。首先按照功能分为4个部分(这样进一步简化了问题),每个功能的实现通过三步来完成,最后把他们集成到一起。

  另外,虽然原来的范例中使用了code first技术,我等菜鸟还是习惯从db first,对localdb不甚了解,这个范例还是先建了数据库MVCShopping,并录入了一些测试数据。

三.按功能具体实现

  依次实现各个部分:

1.商品浏览

  这是最简单通用的一部分,与经典的MVC Movie Store中所呈现的业务逻辑一样。包括:商品类别列表、某类别下的商品列表和某一商品的详细信息。按照实现步骤,分三步实现:

1)数据模型规划

  数据模型涉及到了两个数据实体,商品类别(ProductCategory)和商品信息(Product)。在Models文件夹下新建两个类:ProductCategory和Product

 [DisplayName("商品类别")]
[DisplayColumn("Name")]
public class ProductCategory
{
[Key]
public int Id { get; set; } [DisplayName("商品类别名称")]
[Required(ErrorMessage="请输入商品类别名称")]
[MaxLength(,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(, ErrorMessage = "不得超过60个字")]
public string Name { get; set; } [DisplayName("商品简介")]
[Required(ErrorMessage = "请输入商品简介")]
[MaxLength(, ErrorMessage = "商品简介不超过250字")]
public string Description { get; set; } [DisplayName("商品颜色")]
[Required(ErrorMessage = "请选择颜色")]
public Color Color { get; set; } [DisplayName("商品售价")]
[Required(ErrorMessage = "请输入商品售价")]
[Range(,,ErrorMessage="商品售价必须介于1到10000之间")]
public int Price { get; set; } [DisplayName("上架时间")]
[Description("如果不设置上架时间,代表不发售")]
public DateTime? PublishOn{get;set;}
}

  在这里可能新手会比较困惑的是每个字段上都有形如[DisplayName("商品名称")] [Required]等内容。这些统称为注解,可分为验证注解和显示注解两类。后面会见到很多诸如此类的注解,便于方便和验证。

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()[])</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(, ErrorMessage = "不得超过250个字")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; } [DisplayName("会员密码")]
[Description("密码将以SHA1进行哈西运算,通过运算后的结果转为HEX表示法的字符串长度都为40")]
[Required(ErrorMessage = "请输入密码")]
[MaxLength(, ErrorMessage = "不得超过40个字")]
[DataType(DataType.Password)]
public string Password { get; set; } [DisplayName("中文姓名")]
[Description("忽略外国人")]
[Required(ErrorMessage = "请输入中文姓名")]
[MaxLength(, ErrorMessage = "不得超过5个字")]
public string Name { get; set; } [DisplayName("网络昵称")]
[Required(ErrorMessage = "请输入网络昵称")]
[MaxLength(, ErrorMessage = "不得超过15个字")]
public string Nickname { get; set; } [DisplayName("会员注册时间")]
public DateTime RegisterOn { get; set; } //AuthCode会保存一个GUID值
[DisplayName("会员启用认证码")]
[MaxLength()]
[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 = )
{
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 += ;
}
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 = ;} @foreach (var item in Model)
{
subTotal += item.Product.Price * item.Amount;
var ddlAmountList = new SelectList(Enumerable.Range(,),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>
}

MVC编程实例----简易电子商务网站(一)的更多相关文章

  1. Go语言练习:网络编程实例——简易图片上传网站

    1.代码结构 2.运行实例 1.代码结构 $ tree . ├── photoweb.go ├── public │   ├── css │   ├── images │   └── js ├── u ...

  2. 请求转发:MVC设计模式、细节、请求域属性的编程实例、请求重定向和请求转发的区别

      请求转发:MVC设计模式.细节.请求域属性的编程实例.请求重定向和请求转发的区别 MVC设计模式将一次请求的响应过程分成三个功能模块(一般称之为层)来协同完成,这三个模块分别是Model(模型层) ...

  3. 利用MVC编程模式-开发一个简易记事本app

    学了极客学院一个开发记事本的课程,利用自己对MVC编程模式的简单理解重写了一遍该app. github地址:https://github.com/morningsky/MyNote MVC即,模型(m ...

  4. JAVA上百实例源码网站

    JAVA源码包1JAVA源码包2JAVA源码包3JAVA源码包4 JAVA开源包1 JAVA开源包2 JAVA开源包3 JAVA开源包4 JAVA开源包5 JAVA开源包6 JAVA开源包7 JAVA ...

  5. PHP: 手把手编写自己的 MVC 框架实例教程

    1 什么是MVC MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller ...

  6. hadoop2.2编程:使用MapReduce编程实例(转)

    原文链接:http://www.cnblogs.com/xia520pi/archive/2012/06/04/2534533.html 从网上搜到的一篇hadoop的编程实例,对于初学者真是帮助太大 ...

  7. 简易漫画网站搭建-漫画喵Server版

    小喵的唠叨话:寒假的时候写了一个漫画爬虫,爬取了好几个漫画,不过一直没有找到合适的漫画阅读的工具.因此最近就试着自己写一个漫画的网站,放在公网上或者局域网里,这样就能随时随地用手机.Pad看漫画了. ...

  8. 【原创 Hadoop&Spark 动手实践 6】Spark 编程实例与案例演示

     [原创 Hadoop&Spark 动手实践 6]Spark 编程实例与案例演示 Spark 编程实例和简易电影分析系统的编写 目标: 1. 掌握理论:了解Spark编程的理论基础 2. 搭建 ...

  9. ASP.NET MVC 编程参考

    ASP.NET MVC 编程参考   转载请注明出处:http://surfsky.cnblogs.com MVC    参考 http://msdn.microsoft.com/zh-cn/dd40 ...

随机推荐

  1. Visual studio code离线安装插件

    Visual studio code离线安装插件 公司研发区不能连接公网,使用Visual studio code(vsc)写Golang代码需要安装Go插件,下面介绍下,vsc离线安装插件的步骤.以 ...

  2. python logging一个通用的使用模板

    import os import logbook from logbook.more import ColorizedStderrHandler from functools import wraps ...

  3. vue有关小知识

      截取链接参数: //截取链接参数 this.id = this.$route.query.id;

  4. 房上的猫:if选择结构

    一.基本if结构: 1.定义:if选择结构是根据条件判断之后再做处理的一种语法结构! 2.逻辑:首先对条件进行判断  >如果为真,则执行代码块  >如果为假,执行代码块后面的部分二.常用逻 ...

  5. http协议【转】

    HTTP协议详解 当今web程序的开发技术真是百家争鸣,ASP.NET, PHP, JSP,Perl, AJAX 等等. 无论Web技术在未来如何发展,理解Web程序之间通信的基本协议相当重要, 因为 ...

  6. Java—javac Hello.java找不到文件

    刚开始编写Java代码时,会遇到很多困难,下面来说一个比较常见的错误,如下: 对于初学者,一般都是从Hello,World开始的学起的,废了好大劲儿,铜鼓了半天,终于要在DOS上运行javac Hel ...

  7. Virtualbox虚拟机安装与设置

    Virtualbox与VMware类似,都是虚拟机软件,在win10下安装Virtualbox直接默认安装即可.版本:VirtualBox-5.2.0-118431-Win.exe 安装完成后,点击左 ...

  8. rtx web 分级管理系统 二次开发

    fineui + ASP.NET+rtx server sdk 修正 rtx管理器 修改用户后分级目录出错问题. 加入 单用户多个部门 添加授权关闭部分采用 rsa加密.

  9. iOS学习——UIAlertController详解

    在开发中,弹出提示框是必不可少的.这两天项目中统一对已经被iOS API废弃的UIAlertView和UIActionSheet进行替换,我们知道,UIAlertView和UIActionSheet都 ...

  10. SQLAlchemy框架用法详解

    介绍 SQLAlchemy是一个基于Python实现的ORM框架.该框架建立在 DBAPI之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执 ...