使用Kestrel Web Server
IIS集成
Log
IConfiguration接口
Convertional Routing
使用默认的方式builder.MapRoute("Default", "{Controller}/{Action}/{Id?}");
寻找路由
Attribute Routing
在Controller类或其中的方法上,添加路由信息
namespace Tutorial.Web.Controllers
{
[Route("[controller]")]
public class AboutController
{
public string Me()
{
return "Dave";
}
public string Company()
{
return "No Company";
}
}
}
[Route("[controller]")]
或[Route("stringname/[controller]/[action]")]
根据Controller的名字查找路由,也可以在方法名上定义
[Route("routeName")]
根据自己设定的routeName查找路由
在controller中传入st信息
public IActionResult Index()
{
var st = new Student
{
Id = 1,
FirstName = "Qilong",
LastName = "Wu"
};
return View(st);
}
在index页面,可以使用@Model
获取这个信息的值,注意@Model
首字母大写
cshtml中的@model
,首字母为小写,其代表一个指令,能让Razor视图正确构建代码
如,在Index.cshtml中首行写入
@model Tutorial.Web.Model.Student
则书写
就会有提示信息,提示从controller传过来的数据中都有哪些属性。@Model
@Model.FirstName @Model.LastName @Model.Id
用于输入的Model通常用来创建数据和修改数据,输入Model的方式有以下两种:
使用Form提交Model,提交方式为post,如下例
<form method="post">
<input type="text" name="FirstName" value="" />
<input type="date" name="BirthDate" value="" />
form>
其中input标签中的name属性值FirstName
要和Model中的属性对应
更高级的写法是使用TagHelper的形式,可以自动匹配类型,如date类型。如果不放心可以再声明一遍
<input asp-for="FirstName" />
<input asp-for="BirthDate" type="date"/>
在Student类中添加枚举类型Gender,枚举类型使用TagHelper的方式如下:
<select asp-for="Gender" asp-items="Html.GetEnumSelectList()" >
select>
在以post方式提交的时候,传入的参数为Student类型,asp.net mvc会想方设法把传入参数的属性补全,一般来说,id值自增就不传入id值,自动获取id值就会导致错误,于是使用post方式提交的时候,传入的参数使用重新定义的StudentCreateViewModel类。
//StudentCreateViewModel类
namespace Tutorial.Web.Model
{
public class StudentCreateViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public Gender Gender { get; set; }
}
}
//使用post方式提交,参数使用StudentCreateViewModel,
//然后使用repository服务将创建的学生信息添加到学生列表里。
[HttpPost]
public IActionResult Create(StudentCreateViewModel student)
{
var newStudent = new Student
{
FirstName = student.FirstName,
LastName = student.LastName,
BirthDate = student.BirthDate,
Gender = student.Gender
};
var newModel = repository.Add(newStudent);
return View("Detail", newModel);
}
还需要注意一点的是,在StartUp中,要使用AddSingleton<>()的方式,在项目运行期间都是这一个实例,
不要使用AddScoped,AddScoped每次发出请求都是生成一个新的实例,会导致添加数据添加不上。
services.AddSingleton, InMemoryRepository>();
还有一个问题:当添加成功之后,返回到detial页面,此时刷新网页,会发现id中自增1,这是因为刷新一次页面导致又发出了一个post请求,又产生了新的数据信息。可以使用重定向解决此问题。
//跳转到Detail页面需要传入id信息。
return RedirectToAction(nameof(Detail), new { id = newModel.Id });
关于模型验证的详细内容,参见微软官方文档
使用post方法处理表单
第一步:首先要防止跨站请求伪造CSRF(Cross-site request forgery),需要在方法上方添加
[HttpPost]
[ValidateAntiForgeryToken]
第二步:将验证规则添加到数据模型
Required
:必需属性Display(Name = "xxx")
:提示信息。如果不写则按照label标签中asp-for="xxx"
的值StringLength(60, MinimumLength = 3)
:规定长度RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")
:验证属性是否满足自定义的正则表达式[Range(0,100)]
:范围限制[DataType(DataType.Date)]
指定日期类型,但是日期的格式不做规定。还可以对Password
、Currency
进行指定[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
:指定日期格式CreditCard
、EmailAddress
、Phone
、url
等:验证属性是否具有这种格式如下例
public class StudentCreateViewModel
{
[Required]
[Display(Name = "名")]
public string FirstName { get; set; }
[Display(Name = "姓")]
public string LastName { get; set; }
[Display(Name = "出生日期")]
public DateTime BirthDate { get; set; }
[Display(Name = "性别")]
public Gender Gender { get; set; }
}
第三步:验证错误UI
在使用到Model的view视图中进行错误消息显示
可以在每个input框旁边添加一个span标签以显示错误信息
还可以使用如下代码,对所有的错误验证信息进行输出
或
其中ModelOnly
进队模型中出现的错误验证信息进行显示
第四步:在controller处理
使用if(ModelState.isValid)
,如果验证通过,执行后续的步骤
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(StudentCreateViewModel student)
{
if (ModelState.IsValid)
{
var newStudent = new Student
{
FirstName = student.FirstName,
LastName = student.LastName,
BirthDate = student.BirthDate,
Gender = student.Gender
};
var newModel = repository.Add(newStudent);
//return View("Detail", newModel);
return RedirectToAction(nameof(Detail), new { id = newModel.Id });
}
else
{
ModelState.AddModelError(string.Empty, "Model level Error!");
return View();
}
}
其中视图页面的例子如下
@using Tutorial.Web.Model
@model StudentCreateViewModel
<h1>创建学生h1>
<form method="post">
<div>
<div>
<label asp-for="FirstName">label>
<input asp-for="FirstName" />
<span asp-validation-for="FirstName">span>
div>
<div>
<label asp-for="LastName">label>
<input asp-for="LastName" />
<span asp-validation-for="LastName">span>
div>
<div>
<label asp-for="BirthDate">label>
<input asp-for="BirthDate" type="date" />
<span asp-validation-for="BirthDate">span>
div>
<div>
<label asp-for="Gender">label>
<select asp-for="Gender" asp-items="Html.GetEnumSelectList()" >
select>
div>
div>
<div asp-validation-summary="ModelOnly">div>
<button type="submit" name="save">保存button>
form>
微软官方文档可以查看更详细的信息。
用于对象关系映射ORM
支持的数据库有:
第一步:在appsettings.json文件中添加数据库链接字符串
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Tutorial;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
"CustomConnectionXXX": "Data Source=..."
}
可以添加多个链接,需要用的时候指定名称就行了。
如果使用Visual studio开发,使用的数据库是MSSQLLocalDB,我们只需要指定Initial Catalog
的值就行了,生成的数据表就会存到这个文件夹里。
第二步:创建数据库上下文
需要一个数据库上下文类来协调 Student
模型的 EF Core 功能(创建、读取、更新和删除)。 数据库上下文派生自 Microsoft.EntityFrameworkCore.DbContext 并指定要包含在数据模型中的实体。
使用以下代码添加Data/DataContext.cs文件
namespace Tutorial.Web.Data
{
public class DataContext: DbContext
{
public DataContext (DbContextOptions options): base(options)
{
}
public DbSet Students { get; set; }
}
}
前面的代码为实体集创建DbSet
属性。 在实体框架术语中,实体集通常与数据表相对应。 实体对应表中的行。
DbSet Class用于TEntity实例的查询和保存,对应于DbSet的LINQ查询会被转换成数据库查询
第三步:注册数据库上下文
使用依赖注入的方式,需要在Startup.cs文件中添加服务。
要从appsettings.json文件中获取字符串,可以使用Startup()
构造函数中使用IConfiguration
服务,然后在ConfigureServices()
使用该IConfiguration
服务
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
//获取数据库链接的两种方式
//var connectionString =configuration["ConnectionStrings:DefaultConnection"];
string connectionString = configuration.GetConnectionString("DefaultConnection");
services.AddDbContext(options =>
{
options.UseSqlServer(connectionString);
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton, InMemoryRepository>();
}
第四步:迁移
迁移是可用于创建和更新数据库以匹配数据模型的一组工具。使用visual studio中的Package Manage Console。从“工具”菜单中选择“NuGet 包管理器”>“包管理器控制台 (PMC)” 。
使用如下命令:
Add-Migration InitialDB
然后就会生成 Migrations/{timestamp}_InitialDB.cs 迁移文件,InitialDB
参数是迁移名称, 可以使用任何名称。改文件中的Up
方法创建 Movie 表,并将 Id
配置为主键。 Down
方法可还原 Up
迁移所做的架构更改。
然后输入命令
Update-Database
将数据库更新到上一个命令创建的最新迁移。 此命令执行上述文件中的 Up
方法 。
这样的话,数据表就创建好了
第五步:使用数据库
新建EfCoreRepository服务,实现IRepository接口
namespace Tutorial.Web.Services
{
public class EfCoreRepository : IRepository
{
private readonly DataContext _context;
public EfCoreRepository(DataContext context)
{
_context = context;
}
public IEnumerable GetAll()
{
return _context.Students.ToList();
}
public Student GetById(int id)
{
return _context.Students.Find(id);
}
public Student Add(Student newModel)
{
_context.Students.Add(newModel);
_context.SaveChanges();
return newModel;
}
}
}
将以上服务注册到Startup,使用AddScoped()方法,防止多线程问题。
services.AddScoped, EfCoreRepository>();
然后就可以用了
在每个视图或页面之前运行的代码应置于 _ViewStart.cshtml 文件中。其直接放在Views文件夹下
如果_ViewStart.cshtml 文件中写了如下代码,则为Views文件夹下的所有页面都采用_Layout
布局
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
也可以简写成
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
每个页面也可以具体指定要引用哪个布局
视图和页面可以使用 Razor 指令来导入命名空间并使用依赖项注入。
_ViewImports.cshtml 文件可以放在任何文件夹中,在这种情况下,它只会应用于该文件夹及其子文件夹中的页面或视图。 从根级别开始处理 _ViewImports
文件,然后处理在页面或视图本身的位置之前的每个文件夹。 可以在文件夹级别覆盖根级别指定的 _ViewImports
设置。
_ViewImports
文件支持以下指令:
@addTagHelper
@removeTagHelper
@tagHelperPrefix
@using
@model
@inherits
@inject
示例文件
@using WebApplication1.Models
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
布局文件
其他文件可以用@RenderBody()
来使用此布局文件
布局也可以通过调用 RenderSection
来选择引用一个或多个节
@RenderSection("Scripts", required: false)
分部视图:复用View代码
ASP.NET Core 中的分部视图
视图组件与分部视图类似,但它们的功能更加强大。
ASP.NET Core 中的视图组件
在项目文件下添加npm配置文件package.json,内容如下
{
"version": "1.0.0",
"name": "tutorial",
"private": true,
"devDependencies": {
},
"dependencies": {
"bootstrap": "4.4.1",
"jquery": "3.3.1",
"jquery-validation": "1.19.0",
"jquery-validation-unobtrusive": "3.2.10"
}
}
点击保存之后,依赖项里就会多出依赖的包。
其中jquery-validation
和jquery-validation-unobtrusive
用于前端验证
显示项目的隐藏文件,会发现其实依赖库是安装在了项目文件夹下node_modules文件夹中,而不是wwwroot文件夹中,那么如何伺服这些静态文件呢。
参见 asp.net core 系列之静态文件
可以在Startup.cs中指定,添加如下语句
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = "/node_modules",
FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath,"node_modules"))
});
打开_layout.cshtml页面,添加如下代码
<link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
也可以使用CDN,如BootCDN
一般建议在开发环境下使用本地的库,其他环境下使用CDN库
<environment include="Development">
<link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
environment>
<environment exclude="Development">
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap-grid.css" rel="stylesheet">environment>
参见官方文档:ASP.NET Core 身份验证概述
使用ASP.NET CORE IDENTITY
需要登录注册的view
Register页面和Login页面
AccountController
实现注册、登录、登出
Model
使用LoginViewModel和RegisterViewModel
ASP.NET CORE IDENTITY重点类
配置identity服务
在startup类中的ConfigureServices方法中添加如下代码
//Identity
services.AddDbContext(options =>
{
options.UseSqlServer(connectionString, b => b.MigrationsAssembly("Tutorial.Web"));
});
services.AddDefaultIdentity().AddEntityFrameworkStores();
services.Configure(options =>
{
// 密码设置,为了简单起见,先不对密码做任何限制。
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 0;
options.Password.RequiredUniqueChars = 0;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
});
通过调用UseAuthentication来启用标识。 UseAuthentication
将身份验证中间件添加到请求管道。
在Startup类中的Configure方法中,app.UseMve()
之前添加app.UseAuthentication();
迁移
由于有多个DbContext,所以需要使用-Context
指定具体的Context
Add-Migration InitialIdentity -Context IdentityDbContext
Update database -Context IdentityDbContext
在视图中判断是否登录
@inject SignInManager<IdentityUser> SignInManager
......
<nav class="navbar navbar-light bg-light">
<a class="navbar-brand" href="#">Navbara>
@if (SignInManager.IsSignedIn(User))
{
<form asp-controller="Account" asp-action="Logout" method="post" id="LogoutForm">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a href="javascript:document.getElementById('LogoutForm').submit()">登出a>
li>
ul>
form>
}
else
{
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a asp-controller="Account" asp-action="Register">注册a>
li>
<li class="nav-item">
<a asp-controller="Account" asp-action="Login">登陆a>
li>
ul>
}
nav>
......
用户登录后才能创建学生,需要在HomeController类中的两个Create方法上面添加[Authorize]
[Authorize]
[HttpGet]
public IActionResult Create()
{
return View();
}