一步步打造一个简单的 MVC 电商网站 - BooksStore(四)
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
《一步步打造一个简单的 MVC 电商网站 - BooksStore(一)》
《一步步打造一个简单的 MVC 电商网站 - BooksStore(二)》
《一步步打造一个简单的 MVC 电商网站 - BooksStore(三)》
《一步步打造一个简单的 MVC 电商网站 - BooksStore(四)》
简介
上一节我们完成了两个主要功能:完成了整个购物车的流程,以及订单处理(发邮件进行通知),今天我们来学习一下最基本的增删改查,以及登录认证过滤器,加入防 CSRF 攻击,本系列已完结。
该系列主要功能与知识点如下:
分类、产品浏览、购物车、结算、CRUD(增删改查) 管理、发邮件、分页、模型绑定、认证过滤器和单元测试等。
【备注】项目使用 VS2015 + C#6 进行开发,有问题请发表在留言区哦,还有,页面长得比较丑,请见谅。
目录
基本的增删改查 CRUD
登录授权认证过滤
基本的增删改查 CRUD
我们创建一个新的控制器进行增删改查功能,AdminController,并添加一个显示所有数据的方法:
////// 后台管理控制器 /// public class AdminController : Controller { private readonly IBookRepository _bookRepository; public AdminController(IBookRepository bookRepository) { _bookRepository = bookRepository; } ////// 首页 /// ///public ActionResult Index() { return View(_bookRepository.Books); } }
不在沿用之前的布局页了,创建一个新的布局页 _AdmindLayout.cshtml:
@ViewBag.Title @RenderBody()
Site.css
.table {
width: 100%;
padding: 0;
margin: 0;
}
.table th {
font: bold 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA no-repeat;
}
.table td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
font-size: 14px;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
.table td.alt {
background: #F5FAFA;
color: #797268;
}
.table th.spec, td.spec {
border-left: 1px solid #C1DAD7;
}
对应的Index.cshtml:
@model IEnumerable@{ Layout = "~/Views/Shared/_AdminLayout.cshtml"; } @Html.Actionlink("新增", "Edit")
@foreach (var item in Model) { 名称 描述 价格 分类 } @Html.DisplayFor(modelItem => item.Name) @Html.DisplayFor(modelItem => item.Description) @Html.DisplayFor(modelItem => item.Price) @Html.DisplayFor(modelItem => item.Category) @Html.Actionlink("编辑", "Edit", new { id = item.Id }) @using (Html.BeginForm("Delete", "Admin", FormMethod.Post, new { style = "display:inline;" })) { @Html.Hidden("id", item.Id) }
编辑,我把新增和编辑的位置放在一块,使用 id 进行区分,如果 id = 0 就表示新增的信息。
在 AdminCtroller 中添加关于编辑的方法
////// 编辑 /// /// ///public ActionResult Edit(int id = 0) { if (id == 0) { return View(new Book()); } var model = _bookRepository.Books.FirstOrDefault(x => x.Id == id); return View(model); } /// /// 编辑 /// /// ///[HttpPost] public ActionResult Edit(Book book) { if (!ModelState.IsValid) { return View(book); } _bookRepository.SaveBook(book); return RedirectToAction("Index"); }
更新存储库中的方法:
IBookRepository.cs
////// 书存储库接口 /// public interface IBookRepository { ////// 书模型集合 /// IQueryableBooks { get; } /// /// 保存书 /// /// ///int SaveBook(Book book); /// /// 删除书 /// /// ///Book DeleteBook(int id); }
EfBookRepository.cs
////// 书存储库 /// public class EfBookRepository : IBookRepository { private readonly EfDbContext _context = new EfDbContext(); ////// 书模型集合 /// public IQueryableBooks => _context.Books; /// /// 保存书 /// /// ///public int SaveBook(Book book) { if (book.Id == 0) { _context.Books.Add(book); } else { var model = _context.Books.Find(book.Id); if (model==null) { return 0; } model.Category = book.Category; model.Description = book.Description; model.Name = book.Name; model.Price = book.Price; } return _context.SaveChanges(); } /// /// 删除书 /// /// ///public Book DeleteBook(int id) { var model = _context.Books.Find(id); if (model == null) { return null; } _context.Books.Remove(model); _context.SaveChanges(); return model; } }
需要对 Book 模型加上验证用的特性:
[Table("Book")]
public class Book
{
///
/// 标识
///
public int Id { get; set; }
///
/// 名称
///
[Required(ErrorMessage = "名称不能为空")]
public string Name { get; set; }
///
/// 描述
///
[Required(ErrorMessage = "描述不能为空")]
public string Description { get; set; }
///
/// 价格
///
[Required(ErrorMessage = "价格不能为空")]
[Range(0.01, double.MaxValue, ErrorMessage = "请填写合适的价格")]
public decimal Price { get; set; }
///
/// 分类
///
[Required(ErrorMessage = "分类不能为空")]
public string Category { get; set; }
}
_AdminLayout.cshtml 需要引入验证用的 js(客户端验证):
Edit.cshtml
@model Wen.BooksStore.Domain.Entities.Book
@{
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
编辑
@Html.ValidationSummary()
@using (Html.BeginForm())
{
@Html.HiddenFor(x => x.Id)
| 名称 | @Html.TextBoxFor(x => x.Name) |
| 价格 | @Html.TextBoxFor(x => x.Price) |
| 分类 | @Html.TextBoxFor(x => x.Category) |
| 描述 | @Html.textareaFor(x => x.Description) |
图:错误提示
删除
////// 删除 /// /// ///[HttpPost] public ActionResult Delete(int id) { _bookRepository.DeleteBook(id); return RedirectToAction("Index"); }
加入提示,我们在新增、编辑和删除时应该加入必要的提示信息,使用TempData。
/Admin/Index.cshtml 下的也要添加:
执行效果:
【备注】TempData 临时数据保存了一条信息,是一个“键/值”字典,类似会话 Session 和 ViewBag,它和 Session 的差别是,在 HTTP 请求结束后会被删除。因为这里使用了 RedirectToAction ,一条重定向指令,会告诉浏览器重定向请求到一个新地址,这时就不能使用 ViewBag,ViewBag 用于在控制器与视图之间传递数据,但它保持数据的时间不能比当前的HTTP 请求长,重定向意味着用户是跨请求的,ViewBag 不能用于跨请求时传递数据。
登录授权认证过滤
上面是一个 Admin 的后台管理操作,不是每一个用户都能够进入管理的,所以现在加入登录授权认证功能,只有成功后,才能进入管理界面。
先在配置文件 WebConfig.cs 中加入
WebConfig.cs
在这里使用的授权认证模式为表单认证,为了简化与数据库的交互操作,采取的是硬编码的形式。如果尚未得到认证,会跳转到 Account/Login 的地址让管理员先进行登录,timeout 表示登录(即认证)成功的保持时长为 2880 分钟(即 48 小时),而 name 表示的就是用户名, password 表示的就是登录密码。
这里采用的是授权认证过滤器,我们需要对要认证后才能进入的控制器添加一个特性[Authorize],即对 AdminController 添加该特性。
新建表单认证提供器,一个接口和一个实现:
IAuthProvider.cs:
public interface IAuthProvider
{
///
/// 认证
///
///
///
///
bool Auth(string userName, string password);
}
FormsAuthProvider.cs:
////// 表单认证提供者 /// public class FormsAuthProvider:IAuthProvider { ////// 认证 /// /// /// ///public bool Auth(string userName, string password) { var result = FormsAuthentication.Authenticate(userName, password); if (result) { //设置认证 cookie FormsAuthentication.SetAuthcookie(userName, false); } return result; } }
AddBindings() 方法中注册:
////// 添加绑定 /// private void AddBindings() { _kernel.Bind().To (); _kernel.Bind ().To (); _kernel.Bind ().To (); }
////// 登录视图模型 /// public class LoginViewModel { [Required(ErrorMessage = "用户名不能为空")] public string UserName { get; set; } [Required(ErrorMessage = "密码不能为空")] [DataType(DataType.Password)] public string Password { get; set; } }
新建 AccountController
public class AccountController : Controller
{
private readonly IAuthProvider _authProvider;
public AccountController(IAuthProvider authProvider)
{
_authProvider = authProvider;
}
///
/// 登录
///
///
public ActionResult Login()
{
return View();
}
///
/// 登录
///
///
///
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(new LoginViewModel());
}
var result = _authProvider.Auth(model.UserName, model.Password);
if (result) return RedirectToAction("Index", "Admin");
ModelState.AddModelError("", "账号或用户名有误");
return View(new LoginViewModel());
}
}
Login.cshtml 登录页面:
@model Wen.BooksStore.WebUI.Models.LoginViewModel
@{
Layout = null;
}
登录
@**@
@**@
@@import url(https://fonts.googleapis.com/css?family=Roboto:300);
.login-page {
margin: auto;
padding: 8% 0 0;
width: 360px;
}
.form {
background: #FFFFFF;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
margin: 0 auto 100px;
max-width: 360px;
padding: 45px;
position: relative;
text-align: center;
z-index: 1;
}
.form input {
background: #f2f2f2;
border: 0;
box-sizing: border-box;
font-family: "Roboto", sans-serif;
font-size: 14px;
margin: 0 0 15px;
outline: 0;
padding: 15px;
width: 100%;
}
.form button {
-webkit-transition: all 0.3 ease;
background: #4CAF50;
border: 0;
color: #FFFFFF;
cursor: pointer;
font-family: "Microsoft YaHei", "Roboto", sans-serif;
font-size: 14px;
outline: 0;
padding: 15px;
text-transform: uppercase;
transition: all 0.3 ease;
width: 100%;
}
.form button:hover, .form button:active, .form button:focus { background: #43A047; }
.form .message {
color: #b3b3b3;
font-size: 12px;
margin: 15px 0 0;
}
.form .message a {
color: #4CAF50;
text-decoration: none;
}
.form .register-form { display: none; }
.container {
margin: 0 auto;
max-width: 300px;
position: relative;
z-index: 1;
}
.container:before, .container:after {
clear: both;
content: "";
display: block;
}
.container .info {
margin: 50px auto;
text-align: center;
}
.container .info h1 {
color: #1a1a1a;
font-size: 36px;
font-weight: 300;
margin: 0 0 15px;
padding: 0;
}
.container .info span {
color: #4d4d4d;
font-size: 12px;
}
.container .info span a {
color: #000000;
text-decoration: none;
}
.container .info span .fa { color: #EF3B3A; }
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
background: #76b852;
background: -webkit-linear-gradient(right, #76b852, #8DC26F);
background: -moz-linear-gradient(right, #76b852, #8DC26F);
background: -o-linear-gradient(right, #76b852, #8DC26F);
background: linear-gradient(to left, #76b852, #8DC26F);
font-family: "Roboto", sans-serif;
}
@using (Html.BeginForm("Login", "Account", FormMethod.Post, new { @class = "login-form" }))
{
@Html.ValidationSummary()
@Html.AntiForgeryToken()
@Html.TextBoxFor(x => x.UserName, new { placeholder = "用户名" })
@Html.EditorFor(x => x.Password, new { placeholder = "密码", })
}
【备注】ValidateAntiForgeryToken 特性用于防止跨站请求伪造(CSRF)攻击。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。



