ASP.net Mvc SportsSpore项目开发(六)

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);
        }
    }
}

修改完后查看效果:





项目代码链接:点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨中深巷的油纸伞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值