1.安全性和收尾工作
目前任何人都可以修改物品的管理界面,所以要对管理页面添加安全验证。
首先从配置表单验证开始,这是在ASp.net中的一种验证方法 。
在Webconfig中配置:
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880">
<credentials passwordFormat="Clear">
<user name="admin" password="admin"/>
</credentials>
</forms>
</authentication>
</system.web>
使用了authentication元素建立认证,并用mode属性来指明想要使用表单验证。LoginUrl属性指出了当验证时指向那个页面。timeout指明了验证登录成功后保持登录的时间(默认时间是48小时(2880分钟))。
运用过滤器来进行授权:在Admin控制器中添加授权过滤器:
namespace SportsStore.WebUI.Controllers
{
[Authorize]
public class AdminController : Controller
{
private IProductsRepository repository;
2.创建认证提供器
使用表单认证特性要求调用System.Web.Security.FormsAuthentication类的两个静态方法:
Authenticate方法验证由用户提供的凭据。
SetAuthCookie方法对浏览器的响应添加一个cookie,这样便不需要对用户所做出的的每一个请求都进行认证。
首先从定义认证提供器接口开始。在SportsStore.WebUI项目的Infrastructure文件下创建一个Abstract文件夹,添加一个接口IAuthProvider新接口。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SportsStore.WebUI.Infrastructure.Abstract
{
public interface IAuthProvider
{
bool Authenticate(string username, string password);
}
}
创建该接口的实现在Infrastructure下创建一个Concrete的文件夹,添加一个类FormAuthProvider
using SportsStore.WebUI.Infrastructure.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
namespace SportsStore.WebUI.Infrastructure.Concrete
{
public class FormsAuthprovider : IAuthProvider
{
public bool Authenticate(string username, string password)
{
bool result = FormsAuthentication.Authenticate(username, password);
if (result)
{
FormsAuthentication.SetAuthCookie(username, false);
}
return result;
}
}
}
最后在NinjectDependencyResolver中注册认证提供器
private void AddBindings()
{//绑
kernel.Bind<IProductsRepository>().To<EFProductRepository>();
EmailSettings emailSettings = new EmailSettings
{
WriteAsFile = bool.Parse(ConfigurationManager.AppSettings["Email.WriteAsFile"] ?? "false")
};
kernel.Bind<IOrderProcessor>().To<EmailOrderProcessor>().WithConstructorArgument("settings", emailSettings);
kernel.Bind<IAuthProvider>().To<FormsAuthprovider>();
}
3.创建Account控制器
在SportsStore.WebUI项目的Models文件下添加一个LoginViewModel的类文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace SportsStore.WebUI.Models
{
public class LoginViewModel
{
[Required]
[Display(Name ="用户名")]
public string UserName { get; set; }
[Required]
[Display(Name ="密码")]
public string Password { get; set; }
}
}
创建Account控制器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SportsStore.WebUI.Infrastructure.Abstract;
using SportsStore.WebUI.Models;
namespace SportsStore.WebUI.Controllers
{
public class AccountController : Controller
{
IAuthProvider authProvider;
public AccountController(IAuthProvider auth)
{
authProvider = auth;
}
public ViewResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginViewModel model,string returnUrl)
{
if (ModelState.IsValid)
{
if (authProvider.Authenticate(model.UserName, model.Password))
{
return Redirect(returnUrl ?? Url.Action("Index", "Admin"));
}
else
{
ModelState.AddModelError("", "错误的用户名或密码");
return View();
}
}
else
{
return View();
}
}
}
}
右击Login动作方法添加视图
@model SportsStore.WebUI.Models.LoginViewModel
@{
ViewBag.Title = "Admin:Log In";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<div class="panel text-center">
<div class="panel-heading">
<h3>登陆页面</h3>
</div>
<div class="panel-body">
<p class="lead">请先登陆</p>
@using (Html.BeginForm())
{
@Html.ValidationSummary()
<div class="form-group">
<label>用户名:</label>@Html.TextBoxFor(m=>m.UserName,new { @class="form control"})
</div>
<div class="form-group">
<label>密 码:</label>@Html.TextBoxFor(m => m.Password, new { @class = "form control" })
</div>
<input type="submit" value="登陆"class="btn btn-primary"/>
}
</div>
</div>
4.图像上传
扩展数据库
alter table [dbo].[Products]
add [Imagedata] varbinary(max) null,
[ImageMimeType] varchar(50) null;
增强域模型
在Product模型类下新增图片字段:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace SportsStore.Domain.Entities
{
public class Product
{
[HiddenInput(DisplayValue =false)]
public int ProductID { get; set; }
//[Required(ErrorMessage = "请输入产品名称")]
[Display(Name="名称")]
public string Name { get; set; }
//[Required(ErrorMessage = "请输入产品描述")]
[Display(Name="描述")]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
//[Required]
[Range(0.01,double.MaxValue,ErrorMessage ="请输入一个合理的价格")]
[Display(Name="价格")]
public decimal Price { get; set; }
[Display(Name="物品种类")]
public string Category { get; set; }
public byte[] ImageData { get; set; }
public string ImageMimeType { get; set; }
}
}
创建上传用户界面的元素
在Edit页面修改:
@model SportsStore.Domain.Entities.Product
@{
ViewBag.Title = "Admin:Edit" + @Model.Name;
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<div class="panel">
<div class="panel-heading">
<h3>编辑</h3>
</div>
@using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="panel-body">
@Html.HiddenFor(m => m.ProductID)
@foreach (var property in ViewData.ModelMetadata.Properties)
{
switch (property.PropertyName)
{
case "ProductID":
case "ImageData":
case "ImageMimeType":
break;
default:
<div class="form-group">
<label>
@(property.DisplayName ?? property.PropertyName)
</label>
@if (property.PropertyName == "Description")
{
@Html.TextArea(property.PropertyName, null, new { @class = "form-control", rows = 5 })
}
else
{
@Html.TextBox(property.PropertyName, null, new { @class = "form-control" })
}
@Html.ValidationMessage(property.PropertyName)
</div>
break;
}
}
<div class="form-group">
<div style="position:relative;">
<label>图片</label>
<a class='btn' href='javascript:;'>
选择文件...
<input type="file" name="Image" size="40" style="position:absolute;z-index:2;top:0;left:0;filter:alpha(opacity=0);opacity:0;background-color:transparent;color:transparent;"
οnchange='$("#upload-file-info").html($(this).val());'/>
</a>
<span class='label label-info' id="upload-file-info"></span>
</div>
@if(Model.ImageData == null)
{
<div class="form-control-static">没有图片</div>
}
else
{
<img class="img-thumbnail" width="150" height="150" src="@Url.Action("GetImage","Product",new { Model.ProductID})"/>
}
</div>
</div>
<div class="panel-footer">
<input type="submit" value="保存" class="btn btn-primary" />
@Html.ActionLink("取消", "Index", null, new { @class = "btn btn-default" })
</div>
}
</div>
修改Admin控制器下的Edit方法
[HttpPost]
public ActionResult Edit(Product product,HttpPostedFileBase image = null)
{
if (ModelState.IsValid)
{
if (image != null)
{
product.ImageMimeType = image.ContentType;
product.ImageData = new byte[image.ContentLength];
image.InputStream.Read(product.ImageData, 0, image.ContentLength);
}
repository.SaveProduct(product);
TempData["message"] = string.Format("{0}已保存", product.Name);
return RedirectToAction("Index");
}
else
{
return View(product);
}
}
最后完善修改SportsStore.Domain下的EFProductRepository中的保存信息方法
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
else
{
Product dbEntry = context.Products.Find(product.ProductID);
if (dbEntry != null)
{
dbEntry.Name = product.Name;
dbEntry.Description = product.Description;
dbEntry.Price = product.Price;
dbEntry.Category = product.Category;
dbEntry.ImageData = product.ImageData;
dbEntry.ImageMimeType = product.ImageMimeType;
}
}
context.SaveChanges();
}
5.实现GetImage方法
在ProductController中实现方法
public FileContentResult GetImage(int productId)
{
Product prod = repository.Products.First(p => p.ProductID == productId);
if (prod != null)
{
return File(prod.ImageData, prod.ImageMimeType);
}
else
{
return null;
}
}
最后显示产品图像,在ProductSummary中修改代码
<div class="well">
@if (Model.ImageData != null)
{
<div class="pull-left"style="margin-right:10px">
<img class="img-thumbnail "width="75" height="75" src="@Url.Action("GetImage","Product",new { Model.ProductID})"/>
</div>
}
<h3>
<strong>@Model.Name</strong>
<span class="pull-right label label-primary">@Model.Price.ToString("c")</span>
</h3>
@using (Html.BeginForm("AddToCart", "Cart"))
{//BeginForm 辅助器会创建一个使用HTTPpost方法的表单,也可对其进行修改,使用get方法,但是HTTP规范要求get请求必须是幂等的,也就是说他们不会引起数据的变化,而此处将一个产品添加到购物车显然是一个变化,所以这里没有用get
<div class="pull-right">
@Html.HiddenFor(x=>x.ProductID)
@Html.Hidden("returnUrl",Request.Url.PathAndQuery)
<input type="submit" class="btn btn-success" value="加入购物车"/>
</div>
}
<span class="lead">@Model.Description</span>
</div>
如果跟随本项目一步步做,最后可能会出现一个错误:The model backing the 'EFDbContext' context has changed since the database was created.
这是一个数据库更改后的报错。
如果出现这个问题请按照如下方法修改即可:
在SportsStore.Domain下的Concrete文件夹下的EFDbContext下重写一个方法
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SportsStore.Domain.Entities;
namespace SportsStore.Domain.Concrete
{
public class EFDbContext : DbContext
{
public DbSet<Product> Products {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{//添加此方法,初始化数据库的上下文网关DBcontext,防止数据库更改后的报错
//The model backing the 'EFDbContext' context has changed since the database was created.数据库更改后的报错
Database.SetInitializer<EFDbContext>(null);
base.OnModelCreating(modelBuilder);
}
}
}
修改完后查看效果:
项目代码链接:点击打开链接