一步步打造簡單的MVC電商網(wǎng)站BooksStore(4)
一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(四)
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(一)》
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(二)》
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(三)》
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(四)》
簡介
上一節(jié)我們完成了兩個主要功能:完成了整個購物車的流程,以及訂單處理(發(fā)郵件進行通知),今天我們來學(xué)習一下最基本的增刪改查,以及登錄認證過濾器,加入防 CSRF 攻擊,本系列已完結(jié)。
該系列主要功能與知識點如下:
分類、產(chǎn)品瀏覽、購物車、結(jié)算、CRUD(增刪改查) 管理、發(fā)郵件、分頁、模型綁定、認證過濾器和單元測試等。
【備注】項目使用 VS2015 + C#6 進行開發(fā),有問題請發(fā)表在留言區(qū)哦,還有,頁面長得比較丑,請見諒。
目錄
基本的增刪改查 CRUD
登錄授權(quán)認證過濾
基本的增刪改查 CRUD
我們創(chuàng)建一個新的控制器進行增刪改查功能,AdminController,并添加一個顯示所有數(shù)據(jù)的方法:
/// <summary> /// 后臺管理控制器 /// </summary> public class AdminController : Controller { private readonly IBookRepository _bookRepository; public AdminController(IBookRepository bookRepository) { _bookRepository = bookRepository; } /// <summary> /// 首頁 /// </summary> /// <returns></returns> public ActionResult Index() { return View(_bookRepository.Books); } }
不在沿用之前的布局頁了,創(chuàng)建一個新的布局頁 _AdmindLayout.cshtml:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/Contents/admin/Site.css" rel="stylesheet" /> </head> <body> <div> @RenderBody() </div> </body> </html>
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; }
對應(yīng)的Index.cshtml:
@model IEnumerable<Wen.BooksStore.Domain.Entities.Book> @{ Layout = "~/Views/Shared/_AdminLayout.cshtml"; } <p> @Html.ActionLink("新增", "Edit") </p> <table class="table"> <tr> <th> 名稱 </th> <th> 描述 </th> <th> 價格 </th> <th> 分類 </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.Description) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.Category) </td> <td> @Html.ActionLink("編輯", "Edit", new { id = item.Id }) @using (Html.BeginForm("Delete", "Admin", FormMethod.Post, new { style = "display:inline;" })) { @Html.Hidden("id", item.Id) <input type="submit" value="刪除" /> } </td> </tr> } </table>
編輯,我把新增和編輯的位置放在一塊,使用 id 進行區(qū)分,如果 id = 0 就表示新增的信息。
在 AdminCtroller 中添加關(guān)于編輯的方法
/// <summary> /// 編輯 /// </summary> /// <param name="id"></param> /// <returns></returns> 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); } /// <summary> /// 編輯 /// </summary> /// <param name="book"></param> /// <returns></returns> [HttpPost] public ActionResult Edit(Book book) { if (!ModelState.IsValid) { return View(book); } _bookRepository.SaveBook(book); return RedirectToAction("Index"); }
更新存儲庫中的方法:
IBookRepository.cs
/// <summary> /// 書存儲庫接口 /// </summary> public interface IBookRepository { /// <summary> /// 書模型集合 /// </summary> IQueryable<Book> Books { get; } /// <summary> /// 保存書 /// </summary> /// <param name="book"></param> /// <returns></returns> int SaveBook(Book book); /// <summary> /// 刪除書 /// </summary> /// <param name="id"></param> /// <returns></returns> Book DeleteBook(int id); }
EfBookRepository.cs
/// <summary> /// 書存儲庫 /// </summary> public class EfBookRepository : IBookRepository { private readonly EfDbContext _context = new EfDbContext(); /// <summary> /// 書模型集合 /// </summary> public IQueryable<Book> Books => _context.Books; /// <summary> /// 保存書 /// </summary> /// <param name="book"></param> /// <returns></returns> 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(); } /// <summary> /// 刪除書 /// </summary> /// <param name="id"></param> /// <returns></returns> 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 { /// <summary> /// 標識 /// </summary> public int Id { get; set; } /// <summary> /// 名稱 /// </summary> [Required(ErrorMessage = "名稱不能為空")] public string Name { get; set; } /// <summary> /// 描述 /// </summary> [Required(ErrorMessage = "描述不能為空")] public string Description { get; set; } /// <summary> /// 價格 /// </summary> [Required(ErrorMessage = "價格不能為空")] [Range(0.01, double.MaxValue, ErrorMessage = "請?zhí)顚懞线m的價格")] public decimal Price { get; set; } /// <summary> /// 分類 /// </summary> [Required(ErrorMessage = "分類不能為空")] public string Category { get; set; } }
_AdminLayout.cshtml 需要引入驗證用的 js(客戶端驗證):
<script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/jquery.validate.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
Edit.cshtml
@model Wen.BooksStore.Domain.Entities.Book @{ Layout = "~/Views/Shared/_AdminLayout.cshtml"; } <h2>編輯</h2> <div> @Html.ValidationSummary() <div> @using (Html.BeginForm()) { @Html.HiddenFor(x => x.Id) <table> <tr> <td>名稱</td> <td>@Html.TextBoxFor(x => x.Name)</td> </tr> <tr> <td>價格</td> <td>@Html.TextBoxFor(x => x.Price)</td> </tr> <tr> <td>分類</td> <td>@Html.TextBoxFor(x => x.Category)</td> </tr> <tr> <td>描述</td> <td>@Html.TextAreaFor(x => x.Description)</td> </tr> </table> <input type="submit" value="提交" /> } </div> </div>
圖:錯誤提示
刪除
/// <summary> /// 刪除 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpPost] public ActionResult Delete(int id) { _bookRepository.DeleteBook(id); return RedirectToAction("Index"); }
加入提示,我們在新增、編輯和刪除時應(yīng)該加入必要的提示信息,使用TempData。
/Admin/Index.cshtml 下的也要添加:
執(zhí)行效果:
【備注】TempData 臨時數(shù)據(jù)保存了一條信息,是一個“鍵/值”字典,類似會話 Session 和 ViewBag,它和 Session 的差別是,在 HTTP 請求結(jié)束后會被刪除。因為這里使用了 RedirectToAction ,一條重定向指令,會告訴瀏覽器重定向請求到一個新地址,這時就不能使用 ViewBag,ViewBag 用于在控制器與視圖之間傳遞數(shù)據(jù),但它保持數(shù)據(jù)的時間不能比當前的HTTP 請求長,重定向意味著用戶是跨請求的,ViewBag 不能用于跨請求時傳遞數(shù)據(jù)。
登錄授權(quán)認證過濾
上面是一個 Admin 的后臺管理操作,不是每一個用戶都能夠進入管理的,所以現(xiàn)在加入登錄授權(quán)認證功能,只有成功后,才能進入管理界面。
先在配置文件 WebConfig.cs 中加入
<authentication mode="Forms"> <forms loginUrl="~/Account/Login" timeout="2880"> <credentials passwordFormat="Clear"> <user name="admin" password="123"/> </credentials> </forms> </authentication>
WebConfig.cs
<?xml version="1.0" encoding="utf-8"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=301880 --> <configuration> <connectionStrings> <add name="EfDbContext" connectionString="server=.;database=TestDb;uid=sa;pwd=123" providerName="System.Data.SqlClient"/> </connectionStrings> <appSettings> <add key="webpages:Version" value="3.0.0.0"/> <add key="webpages:Enabled" value="false"/> <add key="ClientValidationEnabled" value="true"/> <add key="UnobtrusiveJavaScriptEnabled" value="true"/> <add key="SendEmailName" value="943239005@qq.com"/> </appSettings> <system.web> <authentication mode="Forms"> <forms loginUrl="~/Account/Login" timeout="2880"> <credentials passwordFormat="Clear"> <user name="admin" password="123"/> </credentials> </forms> </authentication> <compilation debug="true" targetFramework="4.6.1"/> <httpRuntime targetFramework="4.6.1"/> <httpModules> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/> </httpModules> </system.web> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/> <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/> <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/> <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0"/> </dependentAssembly> </assemblyBinding> </runtime> <system.codedom> <compilers> <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"/> <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/> </compilers> </system.codedom> <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules> <remove name="ApplicationInsightsWebTracking"/> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/> </modules> </system.webServer> </configuration>
在這里使用的授權(quán)認證模式為表單認證,為了簡化與數(shù)據(jù)庫的交互操作,采取的是硬編碼的形式。如果尚未得到認證,會跳轉(zhuǎn)到 Account/Login 的地址讓管理員先進行登錄,timeout 表示登錄(即認證)成功的保持時長為 2880 分鐘(即 48 小時),而 name 表示的就是用戶名, password 表示的就是登錄密碼?! ?/p>
這里采用的是授權(quán)認證過濾器,我們需要對要認證后才能進入的控制器添加一個特性[Authorize],即對 AdminController 添加該特性。
新建表單認證提供器,一個接口和一個實現(xiàn):
IAuthProvider.cs:
public interface IAuthProvider { /// <summary> /// 認證 /// </summary> /// <param name="userName"></param> /// <param name="password"></param> /// <returns></returns> bool Auth(string userName, string password); }
FormsAuthProvider.cs:
/// <summary> /// 表單認證提供者 /// </summary> public class FormsAuthProvider:IAuthProvider { /// <summary> /// 認證 /// </summary> /// <param name="userName"></param> /// <param name="password"></param> /// <returns></returns> public bool Auth(string userName, string password) { var result = FormsAuthentication.Authenticate(userName, password); if (result) { //設(shè)置認證 Cookie FormsAuthentication.SetAuthCookie(userName, false); } return result; } }
AddBindings() 方法中注冊:
/// <summary> /// 添加綁定 /// </summary> private void AddBindings() { _kernel.Bind<IBookRepository>().To<EfBookRepository>(); _kernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>(); _kernel.Bind<IAuthProvider>().To<FormsAuthProvider>(); }
/// <summary> /// 登錄視圖模型 /// </summary> 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; } /// <summary> /// 登錄 /// </summary> /// <returns></returns> public ActionResult Login() { return View(); } /// <summary> /// 登錄 /// </summary> /// <param name="model"></param> /// <returns></returns> [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; } <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>登錄</title> @*<link rel="stylesheet" >*@ <link rel="stylesheet" > @*<link href="~/Contents/Login/css/htmleaf-demo.css" rel="stylesheet" />*@ <style type="text/css"> @@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; /* fallback for old browsers */ 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; } </style> <!--[if IE]> <script src="http://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <![endif]--> <script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/jquery.validate.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.js"></script> </head> <body> <div id="wrapper" class="login-page"> <div id="login_form" class="form"> @using (Html.BeginForm("Login", "Account", FormMethod.Post, new { @class = "login-form" })) { <span style="float: left; color: red;">@Html.ValidationSummary()</span> @Html.AntiForgeryToken() @Html.TextBoxFor(x => x.UserName, new { placeholder = "用戶名" }) @Html.EditorFor(x => x.Password, new { placeholder = "密碼", }) <input type="submit" value="登 錄" /> } </div> </div> </body> </html>
【備注】ValidateAntiForgeryToken 特性用于防止跨站請求偽造(CSRF)攻擊。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習有所幫助,也希望大家多多支持腳本之家。
- 一步步打造簡單的MVC電商網(wǎng)站BooksStore(3)
- 一步步打造簡單的MVC電商網(wǎng)站BooksStore(1)
- MVC4制作網(wǎng)站教程第四章 更新欄目4.3
- MVC4制作網(wǎng)站教程第四章 添加欄目4.1
- asp.net mvc驗證碼類使用
- MVC使用極驗驗證制作登錄驗證碼學(xué)習筆記7
- ASP.NET MVC驗證碼功能實現(xiàn)代碼
- ASP.NET?MVC5網(wǎng)站開發(fā)之添加、刪除、重置密碼、修改密碼、列表瀏覽管理員篇2(六)
- ASP.NET MVC5網(wǎng)站開發(fā)之登錄、驗證和注銷管理員篇1(六)
- MVC+EasyUI+三層新聞網(wǎng)站建立 建站準備工作(一)
相關(guān)文章
.Net Core 實現(xiàn)圖片驗證碼的實現(xiàn)示例
這篇文章主要介紹了.Net Core 實現(xiàn)圖片驗證碼的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2020-03-03.net mvc頁面UI之Jquery博客日歷控件實現(xiàn)代碼
最近在做一個博客系統(tǒng),其他需要用到博客日歷控件,網(wǎng)上搜索了很多資料,其中大部分都是javascript的,經(jīng)過總結(jié)使用jquery實現(xiàn)了博客日歷效果。代碼如下2013-09-09ASP.NET MVC中SignalR的簡單應(yīng)用
這篇文章主要為大家詳細介紹了ASP.NET MVC中SignalR的簡單應(yīng)用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07ASP.NET2.0緩存(Cache)技術(shù)深入理解
緩存技術(shù)是ASP.NET2.0非常重要的一個特性,它提供了一種非常好的本地數(shù)據(jù)緩存機制,從而有效的提高數(shù)據(jù)訪問的性能2012-11-11asp.net中Word轉(zhuǎn)Html的辦法(不需要WORD組件)
最近碰到一個需求,在文本編輯器中實現(xiàn)“Word導(dǎo)入”功能 發(fā)現(xiàn)了幾種方法,只是作為總結(jié)使用2013-04-04Gridview使用CheckBox全選與單選采用js實現(xiàn)同時高亮顯示選擇行
Gridview使用CheckBox單選與全選功能再次進行簡單演示,選中的行,使用高亮顯示,讓用戶一目了然看到哪一行被選擇了,在項目中很實用的,開發(fā)中的朋友們可要考慮一下哦2013-01-01