ASP.NET Identity 提供了许多的 API接口供使用,但是都是以继承的方式来实现的。因此,在使用某个功能之前,需要继承该功能的基类。
1. 继承 IdentityUser
用户是 ASP.NET Identity 其它操作的基础,所以在使用 ASP.NET Identity 之前,首先要创建 User 类。在 ASP.NET Identity 中,用户的基本信息是很容易扩展的,也就是可以添加我们自己的用户属性,如 QQ、微信、爱好、职业等等。
IdentityUser类是定义在Microsoft.AspNet.Identity.EntityFramework名称空间下的:
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole,
IdentityUserClaim>, IUser, IUser<string>
{
public IdentityUser();
public IdentityUser(string userName);
}
}
从定义上看,IdentityUser 类是继承了泛型的 IdentityUser<>、IUser 和泛型的 IUser<>接口。而 IUser 接口又是继承了 IUser<string>泛型接口。如下代码:
namespace Microsoft.AspNet.Identity
{
//
// 摘要:
// Minimal interface for a user with id and username
//
// 类型参数:
ASP.NET Identity 的配置及在 MVC5 框架中的应用详细开发实战培训教程
22 / 136
一都 (Yidosoft.cn) 软件
// TKey:
public interface IUser<out TKey>
{
//
// 摘要:
// Unique key for the user
TKey Id { get; }
//
// 摘要:
// Unique username
string UserName { get; set; }
}
}
在泛型的 IUser 接口中,定义了 Id 和 UserName 属性,并且是唯一值。而更多的用户属性是定义在泛型的 IdentityUser 基类中的。如下代码:
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
{
public IdentityUser();
public virtual int AccessFailedCount { get; set; }
public virtual ICollection<TClaim> Claims { get; }
public virtual string Email { get; set; }
public virtual bool EmailConfirmed { get; set; }
public virtual TKey Id { get; set; }
public virtual bool LockoutEnabled { get; set; }
public virtual DateTime? LockoutEndDateUtc { get; set; }
public virtual ICollection<TLogin> Logins { get; }
public virtual string PasswordHash { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual bool PhoneNumberConfirmed { get; set; }
public virtual ICollection<TRole> Roles { get; }
public virtual string SecurityStamp { get; set; }
public virtual bool TwoFactorEnabled { get; set; }
public virtual string UserName { get; set; }
}
}
在这里可以看到 User 的所有自带的相关属性,另外,还可以看到 IdentityUser 还受到了 IdentityUserLogin、IdentityUserRole、IdentityUserClaim 的约束。
现在我们在“Yidosoft.Identity”项目的“Models”文件夹下添加一个名称为“User”的类。然后继承 IdentityUser 基类。代码如下:
using Microsoft.AspNet.Identity.EntityFramework;
namespace Yidosoft.Identity.Models
{
public class User : IdentityUser
{
}
}
而我们自定义的扩展的用户属性,只需要在 User 类中编写即可。如下代码,我们添加用户的 QQ 和微信号属性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using System.Security.Claims;
namespace jsdhh2.Models
{
public class User: IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
{
// 请注意,authenticationType 必须与 CookieAuthenticationOptions.AuthenticationType 中定义的相应项匹配
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// 在此处添加自定义用户声明
return userIdentity;
}
#region 添加字段
public virtual string WX { get; set; }
public virtual string QQ { get; set; }
public virtual DateTime CreateTime { get; set; }
public virtual int? DepartmentId { get; set; }
public virtual string Address { get; set; }
public virtual int? Gender { get; set; }
public virtual DateTime? BirthDate { get; set; }
public virtual string TheHour { get; set; }
public virtual DateTime? DetailedTime { get; set; }
public virtual string RealName { get; set; }
public virtual int? SpouseId { get; set; }
public virtual string HeaderPic { get; set; }
#endregion
}
}
这样在使用 Entity Framework 的 Code First 特性时,就会将这些自定义的属性映射为相应的字段。
2. 继承 IdentityDbContext<User>
ASP.NET Identity 是使用 Entity Framework 来操作并与数据库交互的。所以需要创建数据上下文类,并且该类必须是继承了 IdentityDbContext<T>类的,并且泛型类中的类型参数 T 必须是在 4.1 节中创建的 User 类,User 类必须是继承了IdentityUser 类的。它们之间都存在约束,可见这也呈现的编程的严谨性。
这里也将数据上下文类创建在“Models”文件夹中,当然如果你的项目具有分层结构,则可以放在合适的层中,这里重点是讲解 ASP.NET Identity,不存在分层。
在项目中新建一个文件夹DAL。在DAL中新建一个名称为“IdentityDbContext”的类文件,并继承 IdentityDbContext<User>类。IdentityDbContext<T>也是定义在:Microsoft.AspNet.Identity.EntityFramework 名称空间下的。如下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using jsdhh2.Models;
using Microsoft.AspNet.Identity.EntityFramework;
namespace jsdhh2.DAL
{
public class IdentityDbContext : IdentityDbContext<User>
{
}
}
然后在构造函数中传入数据库连接字符串或 web.config 配置文件中配置的连接字符串的名称。建议使用连接字符串的名称,这样以后变更了数据库连接字符串,直接在 web.config 文件中修改即可。最终的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using jsdhh2.Models;
using Microsoft.AspNet.Identity.EntityFramework;
namespace jsdhh2.DAL
{
public class IdentityDbContext : IdentityDbContext<User>
{
public IdentityDbContext() : base("MyOaContent", throwIfV1Schema: false)
{
}
/// <summary>
/// 实例化一个IdentityDbContext。
/// </summary>
/// <returns></returns>
public static IdentityDbContext Create()
{
return new IdentityDbContext();
}
}
}
在此代码中,IdentityDbContext 构造函数使用基类的构造函数传入参数。基类构造函数代码如下:
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class IdentityDbContext<TUser> : IdentityDbContext<TUser, IdentityRole, string,
IdentityUserLogin, IdentityUserRole, IdentityUserClaim> where TUser : IdentityUser
{
public IdentityDbContext();
public IdentityDbContext(string nameOrConnectionString);
public IdentityDbContext(DbCompiledModel model);
public IdentityDbContext(string nameOrConnectionString, bool throwIfV1Schema);
public IdentityDbContext(DbConnection existingConnection, bool
contextOwnsConnection);
public IdentityDbContext(string nameOrConnectionString, DbCompiledModel model);
public IdentityDbContext(DbConnection existingConnection, DbCompiledModel model,
bool contextOwnsConnection);
}
}
这里使用的是第 4 个构造函数,包含两个参数:一个是数据库连接字符串的名称或连接字符串,这里使用 web.config 配置文件中的名称 YDDbConnection。另一个是布尔类型 throwIfV1Schema 参数,表示如果模式匹配 Identity 1.0.0,是否将抛出一个异常,这里设置为 false。另外,还有一个静态方法 Create(),用来实例化 Identity DbContext 数据上下文。
3. 配置数据库连接字符串
打开“Yidosoft.Identity”项目中的 web.config 文件,在<configuration>节点添加<connectionStrings>节点。如下代码:
<configuration>
<configSections>
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework,
Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</configSections>
<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" />
</appSettings>
<connectionStrings>
<add name="MyOaContent" connectionString="Data Source=192.168.1.206;Initial Catalog=YDIdentityDb;uid=sa;pwd=12345678" providerName="System.Data.SqlClient"/> </connectionStrings> </configuration>
注意:这里的 name 属性的值一定要与在 IdentityDbContext 构造函数中配置的名称完全一致。并且数据库的名称可以任意起,不必事先在数据库中创建好,EntityFramework 会帮我们创建。
4. 继承 UserManager<User>
UserManager<T>泛型类是一个用来管理用户的类,T 就是我们在 4.1 节中创建的User 类,并且 User 类必须继承 IdentityUser 类。同样我们也需要创建一个类并继承该基类。
在“Yidosoft.Identity”项目的“App_Start”文件夹中创建一个名称为“IdentityConfig”的类文件,并将自动创建的“IdentityConfig”类删除掉,名称空间修改为:Yidosoft.Identity。“IdentityConfig.cs”用来配置与 Identity 相关的操作。然后添加一个名称为“UserManager”的类,并继承 UserManager<User>基类。代码如下:(备注:要注意把ROLES的先注释“)
实际上用户的操作都是定义在 IUserStore<User>接口中的,所以必须在UserManager 构造函数中将 IUserStore<User>作为参数传入。最终的代码修改如
using jsdhh2.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using System.Security.Claims;
namespace jsdhh2
{
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
//编写发送邮件的代码
//Common.SendMail("smtp.126.com", 25, "lbj$4561200", "yidosoft@126.com", message.Destination, message.Subject, message.Body);
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// 在此处插入 SMS 服务可发送短信。
return Task.FromResult(0);
}
}
/// <summary>
/// 配置用户管理器
/// </summary>
public class UserManager : UserManager<User>
{
public UserManager(IUserStore<User> store) : base(store) { }
public static UserManager Create(IdentityFactoryOptions<UserManager> options, IOwinContext context)
{
var manager = new UserManager(new UserStore<User>(context.Get<DAL.IdentityDbContext>()));
manager.EmailService = new EmailService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("EmailConfirmation"));
}
return manager;
}
}
/// <summary>
/// 配置应用程序登录管理器
/// </summary>
public class SignInManager : SignInManager<User, string>
{
public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(User user)
{
return user.GenerateUserIdentityAsync((UserManager)UserManager);
}
public static SignInManager Create(IdentityFactoryOptions<SignInManager> options, IOwinContext context)
{
return new SignInManager(context.GetUserManager<UserManager>(), context.Authentication);
}
}
/// <summary>
/// 配置角色管理器
/// </summary>
//public class RoleManager : RoleManager<Role>
//{
// public RoleManager(RoleStore<Role> store) : base(store) { }
// public static RoleManager Create(IdentityFactoryOptions<RoleManager> options, IOwinContext context)
// {
// var manager = new RoleManager(new RoleStore<Role>(context.Get<Models.IdentityDbContext>()));
// return manager;
// }
//}
}
这里还有一个静态的 Create()方法,用于实例化 UserManager 类。用来管理用户的所有相关操作。
5. 注册 UserManager
配置身份验证主要是使用 CreatePerOwinContext()方法配置数据库上下文和用户管理器,以便为每个请求使用单个实例。
在“Yidosoft.Identity”项目的“App_Start”文件夹中添加一个名称为“Startup.Auth.cs”的部分(partial)类文件,类名为“Startup”,作为 OWIN Startup 类的一部分,名称空间修改为:Yidosoft.Identity。然后编写如下代码:
using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Google;
using Owin;
using jsdhh2.Models;
using jsdhh2.DAL;
namespace jsdhh2
{
public partial class Startup
{
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// 配置数据库上下文、用户管理器和登录管理器,以便为每个请求使用单个实例
//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.CreatePerOwinContext(IdentityDbContext.Create);
//注册UserManger管理器
app.CreatePerOwinContext<UserManager>(UserManager.Create);
//注册SignInManager管理器
app.CreatePerOwinContext<SignInManager>(SignInManager.Create);
//注册RoleManager管理器
//app.CreatePerOwinContext<RoleManager>(RoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/User/Login")
});
}
在此代码中,还使用了 DefaultAuthenticationTypes.ApplicationCookie 配置默认的认证类型是ApplicationCookie。并且使用LoginPath指定认证失败时转向到的URL。
6. 创建 OWIN Startup
备注,我一般以新建WEB项目时,选了个人验证方式,他自动生成了这些类,不用新建。
在 Visual Studio 2015 中,是可以直接创建“OWIN Startup 类”的。右击“Yidosoft.Identity”项目,如图 4-1 所示:
在图 4-1 中点击“添加”“OWIN Startup 类”,如图 4-2 所示:
由于我们在“Startup.Auth.cs”文件中创建了部分类 Startup,所以在“Startup.cs”文件中的“Startup”类也必须是部分类。完整的代码如下:
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(jsdhh2.Startup))]
namespace jsdhh2
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
这里只是在Configuration()方法中调用在Startup.Auth.cs中编写的ConfigureAuth()方法,并将 IAppBuilder 类型的参数传入。创建的 OWIN Startup 类作为身份验证的启动类。主要是与 OWIN 相关的配置。
5. 创建控制器
在“Yidosoft.Identity”项目的“Controllers”文件夹上右击,点击“添加”“控制器”,如图 5-2 所示:
选择“MVC 5 控制器-空”,点击“添加”按钮。如图 5-3 所示:
中将控制器名称修改为“UserController”,点击“添加”按钮。
在控制器中可以使用 HttpContext.GetOwinContext().Get()方法将使用CreatePerOwinContext()方法注册的IdentityDbContext 和UserManager的实例获取。
然后就可以在控制器中使用了。如下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using jsdhh2.Models;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity;
using jsdhh2.ViewModels;
using jsdhh2;
namespace Yidosoft.Identity.Controllers
{
public class UserController : Controller
{
private UserManager _userManager;
public UserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<UserManager>();
}
private set
{
_userManager = value;
}
}
private SignInManager _signInManager;
public SignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<SignInManager>();
}
private set
{
_signInManager = value;
}
}
// GET: User
public ActionResult Index()
{
return View();
}
[HttpGet]
[AllowAnonymous]
public ActionResult Add()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Add(AddUserViewModel addUserModel)
{
if (ModelState.IsValid)
{
var _creatTime = DateTime.Now;
var _headerPic = "/Content/noheaderpic.png";
var user = new User { UserName = addUserModel.UserName, Email = addUserModel.Email, PhoneNumber = addUserModel.PhoneNumber, CreateTime =_creatTime, HeaderPic = _headerPic };
var result = await UserManager.CreateAsync(user, addUserModel.Password);
if (result.Succeeded)
{
//登录
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// 发送包含此链接的电子邮件
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "User", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "确认你的帐户", "请通过单击 <a href=\"" + callbackUrl + "\">这里</a>来确认你的帐户");
//return RedirectToAction("List", "User");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
return View(addUserModel);
}
//[HttpGet]
//public ActionResult List()
//{
// var users = UserManager.Users.ToList();
// return View(users);
//}
//[HttpGet]
//public ActionResult Edit(string id)
//{
// if (string.IsNullOrWhiteSpace(id))
// {
// return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest);
// }
// User user = UserManager.FindById(id);
// if (user == null)
// {
// return HttpNotFound();
// }
// var editUserViewModel = new EditUserViewModel()
// {
// Email = user.Email,
// QQNumber = user.QQNumber,
// WechatNumber = user.WechatNumber
// };
// return View(editUserViewModel);
//}
//[HttpPost]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> Edit(string id, EditUserViewModel editUserViewModel)
//{
// if (ModelState.IsValid && !string.IsNullOrEmpty(id))
// {
// User user = UserManager.FindById(id);
// user.Email = editUserViewModel.Email;
// user.QQNumber = editUserViewModel.QQNumber;
// user.WechatNumber = editUserViewModel.WechatNumber;
// user.UserName = editUserViewModel.Email;
// var result = await UserManager.UpdateAsync(user);
// if (result.Succeeded)
// {
// return RedirectToAction("List", "User");
// }
// AddErrors(result);
// }
// return View(editUserViewModel);
//}
///// <summary>
///// 登录
///// </summary>
///// <param name="returnUrl"></param>
///// <returns></returns>
//[HttpGet]
//[AllowAnonymous]
//public ActionResult Login(string returnUrl)
//{
// ViewBag.ReturnUrl = returnUrl;
// return View();
//}
///// <summary>
///// 登录
///// </summary>
///// <param name="model"></param>
///// <param name="returnUrl"></param>
///// <returns></returns>
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
//{
// if (!ModelState.IsValid)
// {
// return View(model);
// }
// // 这不会计入到为执行帐户锁定而统计的登录失败次数中
// // 若要在多次输入错误密码的情况下触发帐户锁定,请更改为 shouldLockout: true
// var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
// switch (result)
// {
// case SignInStatus.Success:
// return RedirectToLocal(returnUrl);
// case SignInStatus.LockedOut:
// return View("Lockout");
// case SignInStatus.RequiresVerification:
// return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
// case SignInStatus.Failure:
// default:
// ModelState.AddModelError("", "无效的登录尝试。");
// return View(model);
// }
//}
///// <summary>
///// 邮箱验证
///// </summary>
///// <param name="userId"></param>
///// <param name="code"></param>
///// <returns></returns>
//[HttpGet]
//[AllowAnonymous]
//public async Task<ActionResult> ConfirmEmail(string userId, string code)
//{
// if (userId == null || code == null)
// {
// return View("Error");
// }
// var result = await UserManager.ConfirmEmailAsync(userId, code);
// return View(result.Succeeded ? "ConfirmEmail" : "Error");
//}
///// <summary>
///// 忘记密码
///// </summary>
///// <returns></returns>
//[AllowAnonymous]
//public ActionResult ForgotPassword()
//{
// return View();
//}
///// <summary>
///// 忘记密码
///// </summary>
///// <param name="model"></param>
///// <returns></returns>
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
//{
// if (ModelState.IsValid)
// {
// var user = await UserManager.FindByNameAsync(model.Email);
// if (user == null)
// {
// return View("Error");
// }
// //发送包含此链接的电子邮件
// string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
// var callbackUrl = Url.Action("ResetPassword", "User", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "重置密码", "请通过单击 <a href=\"" + callbackUrl + "\">此处</a>来重置你的密码");
// return RedirectToAction("ForgotPasswordConfirmation", "User");
// }
// // 如果我们进行到这一步时某个地方出错,则重新显示表单
// return View(model);
//}
//[AllowAnonymous]
//public ActionResult ForgotPasswordConfirmation()
//{
// return View();
//}
///// <summary>
///// 重置密码
///// </summary>
///// <param name="code"></param>
///// <returns></returns>
//[HttpGet]
//[AllowAnonymous]
//public ActionResult ResetPassword(string code)
//{
// return code == null ? View("Error") : View();
//}
///// <summary>
///// 重置密码
///// </summary>
///// <param name="model"></param>
///// <returns></returns>
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
//{
// if (!ModelState.IsValid)
// {
// return View(model);
// }
// var user = await UserManager.FindByNameAsync(model.Email);
// if (user == null)
// {
// return View("Error");
// }
// var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
// if (result.Succeeded)
// {
// return RedirectToAction("ResetPasswordConfirmation", "User");
// }
// AddErrors(result);
// return View();
//}
//[AllowAnonymous]
//public ActionResult ResetPasswordConfirmation()
//{
// return View();
//}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
//private ActionResult RedirectToLocal(string returnUrl)
//{
// if (Url.IsLocalUrl(returnUrl))
// {
// return Redirect(returnUrl);
// }
// return RedirectToAction("Index", "Home");
//}
}
}
public SignInManager SignInManager
using jsdhh2;
namespace Yidosoft.Identity.Controllers
在控制器的方法中,就可以使用 UserManager 属性进行用户管理了。
6. 添加客户端验证(这步我也没安装自带的就有)
此步骤的操作也可以在空的 MVC 5 项目中第一次添加视图(也就是后面讲解的7.1.3 节)之后再操作。这里作为独立的一个章节拿出来讲解。在 ASP.NET MVC 5 空项目中,在第一次添加视图时,Visual Studio 2015 会自动添加一些与 UI 相关的文件,包括 Bootstrap、Jquery、modernizr 等。但是就没有添加与客户端验证相关的 JS 文件。默认情况下,ASP.NET MVC 5 是支持客户端和服务器端双重数据验证的。服务器端数据验证默认就支持。现在我们添加与客户端相关的 JS 文件。
ASP.NET MVC 5 的客户端数据验证是需要 jquery.validate.js 和jquery.validate.unobtrusive.js 这两个 JS 文件支持的。“Microsoft.jQuery.Unobtrusive.Validation”,这是由微软在 jQuery 的基础上编写的在 ASP.NET MVC 5 下使用的验证 JS 文件。可以通过 NuGet 包管理器安装这 2 个验证文件。如图 6-1 所示:
圈住的两个包安装。安装完成后如图 6-2 所示:
安装成功后,会在项目的“Scripts”文件夹中添加图 6-2 红线标注的 5 个文件,其
中还包括了 min.js 类型的文件。
打开“_Layout.cshtml”视图,将:
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
放在原 JS 文件的底部。如图 6-3 所示:
由于 jquery.validate.js 是依赖于 jquery.js 文件的,所以要放在 jquery.js 文件后面
才会生效
_Layout布局上加上:
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>