.NET5从零基础到精通:全面掌握.NET5开发技能【第三章】
章节
第一章:https://www.cnblogs.com/kimiliucn/p/17613434.html
第二章:https://www.cnblogs.com/kimiliucn/p/17620153.html
第三章:https://www.cnblogs.com/kimiliucn/p/17620159.html
十三、权限验证
13.1-基于Seesion/Cookies的权限认证
为了拦截一些操作:
- 传统的授权方式:Seesion、Cookies来完成:
- 在请求某个Action之前来做校验,验证当前操作者是否登录过,登录过就有权限。
- 如果没有权限就调到到登录页面去。
- AOP-Filter、ActionResult
13.1.1-传统的登录需要匿名
当注册全局权限验证码的时候,需要将Login取消权限验证,不然会产生这个错误:
解决:
(1)在【CustomActionAuthrizaFilterAttribute】中写代码写上这个代码支持匿名,继承自Attribute,实现IActionFilter接口。
(2)在Login的Action写上这个特性。
13.2-基于鉴权授权
通过中间件来支持;
(1)在【Startup.cs】中创建的【Configure】方法中进行中间件,使用中间件使用在App.UseRouting()之后,在app.UseEndpoints()之前。
#region 第一步:告诉框架说我要使用鉴权授权功能
app.UseAuthentication();//鉴权:检测有没有登录,登录的是谁,赋值给User
app.UseAuthorization();//授权:检测有没有权限,是否能够访问后续的页面
#endregion
(2)在【Startup.cs】中创建的【ConfigureServices】方法中增加一个AddAuthentication。
#region 增加一个授权AddAuthentication
//用Cookie
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
//如果授权是被,就跳转到这个路径中
options.LoginPath = new PathString("Eighth/Login");
});
#endregion
(3)指定哪些Action需要做鉴权授权,直接标记特性;可以标记在控制器、Action,也可以标记在全局。
(4)在控制器的Login登录方法中修改如下:
#region 鉴权:鉴权,检测有没有登录,登录的是谁,赋值给User
//rolelist 是登录成功后用户的角色---是来自于数据库的查询;不同的用户会查询出不同的角色;
var rolelist = new List<string>() {
"Admin",
"Teacher",
"Student"
};
//ClaimTypes.Role就是做权限认证的标识;
var claims = new List<Claim>()//鉴别你是谁,相关信息
{
new Claim(ClaimTypes.Role,"Admin"),
new Claim(ClaimTypes.Name,name),
new Claim("password",password),//可以写入任意数据
new Claim("Account","Administrator"),
new Claim("role","admin"),
new Claim("admin","admin"),
};
foreach (var role in rolelist)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
//将用户放在ClaimsPrincipal里面去了;相当身份证,将信息写到身份证里面去;
ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer"));
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(30),//过期时间:30分钟
}).Wait();
#endregion
(5)当注册到控制器上、或者注册到全局上,又会出现这样的问题。
解决:在Login的Action上加上匿名特性,这个匿名是来自于框架的;
[AllowAnonymousAttribute] //匿名
13.3-鉴权授权-角色授权(目前看是将角色定义死了)
不同的用户,可能会存在不同的角色,然而不同的角色在访问不同的页面的时候,需要做不同的拦截。——角色授权其实就是通过角色不同,做不通的权限拦截。
1.保证上一个(13.2)代码是不变。
2.在Action需要加上对应拥有访问该视图的角色。
当访问【Index03】时,会被拦截,跳转到拦截页面:
13.4-鉴权授权-策略授权
之前的角色授权是在代码中把【角色】定义死了,我们更希望能够自己来完成校验逻辑:
(1)创建一个类为【CustomAuthorizationHandler】,这个类专用来检验逻辑的,要求继承自【AuthorizationHandler<>】泛型类(泛型类中要求实现【IAuthorizationRequirement】接口:见步骤2)然后在CustomAuthorizationHandler类中实现【HandleRequirementAsync】抽象方法,写逻辑。
public class CustomAuthorizationHandler : AuthorizationHandler<CustomAuthorizationRequirement>
{
public CustomAuthorizationHandler() {
}
/// <summary>
/// 实现抽象方法
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizationRequirement requirement)
{
if (requirement.Name == "Policy01") {
//策略1的逻辑
}
if (requirement.Name == "Policy02")
{
//策略2的逻辑
}
if (requirement.Name == "Policy03")
{
//策略3的逻辑
}
if (true) {
}
//在这里可以定义自己的规则
{
/*
其实这里可以去数据库里面做一些查询,然后根据用户的信息,做计算:如果符合就context.Succd(requirement)
否则就Task。CompletedTask
*/
}
//context.User 鉴权成功(登录成功后),用户的信息
var role = context.User.FindFirst(u => u.Value.Contains("admin"));
if (role != null) {
context.Succeed(requirement);//验证通过了
}
return Task.CompletedTask;//验证不通过
}
}
(2)创建一个为类【CustomAuthorizationRequirement】,改类实现【IAuthorizationRequirement】接口,用来放在AuthorizationHandler<>泛型类中。
(3)怎么让其生效?在【Startup.cs】中创建的【ConfigureServices】方法中注册支持策略授权,也可以支持多种策略认证。
#region 支持多种策略认证
services.AddAuthorization(options =>
{
options.AddPolicy("customPolicy", policy =>
{
policy.AddRequirements(new CustomAuthorizationRequirement("Policy01"));
});
});
services.AddAuthorization(options =>
{
options.AddPolicy("customPolicy01", policy =>
{
policy.AddRequirements(new CustomAuthorizationRequirement("Policy02"));
});
});
services.AddAuthorization(options =>
{
options.AddPolicy("customPolicy02", policy =>
{
policy.AddRequirements(new CustomAuthorizationRequirement("Policy03"));
});
});
#endregion
//注册支持策略授权
services.AddSingleton<IAuthorizationHandler, CustomAuthorizationHandler>();
十四、跨平台本质
- web应用程序是一个控制台;——Main程序的入口
- 跨平台的主要原因:
跨平台的原因在于框架已经内置了一个主机,只要是程序启动,就是启动了主机,就可以监听端口;请求来了,只要是请求这个端口,主机就可以相应,所以无论是Windows还是在Linux下开发,都是要第一步完成环境的;这样就不同拘泥于在Windows还是Linux上。
十五、中间件
15.1-什么是中间件?
- 中间件的执行,是一个俄罗斯套娃;
- 先Use先执行,每一次Use一个中间件,其实就是在之前的基础上,套了一层;
- 请求来了以后,真正执行的时候,是一层一层的内部执行,在执行出来。
增加程序的扩展性:
如果想要增加一层,直接增加一个中间件就可以来完成。
15.2-常用的中间件
15.2.1-app.Run:中断式,只要使用当前中间件,后面的中间件都不执行;
15.2.2-app.Map:判断路径中包含什么内容。
15.2.3-app.MapWhen:判断式,两个委托,第一个委托做为判断条件内容,第二个委托,是要执行的逻辑
15.3-中间件扩展-引用
(1)中间件的内容可以独立开,放在一个独立的类中,需要有一定的规则如下(创建了3个中间件):
/// <summary>
/// 要求构造函数带有RequestDelegate参数类型——目的是为了得到下一个中间件;
/// 必须包含async Task Invoke方法,方法参数为HttpContext
/// </summary>
public class FirstMiddleware
{
private readonly RequestDelegate _next;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="next"></param>
public FirstMiddleware(RequestDelegate next) {
_next = next;
}
public async Task Invoke(HttpContext context) {
await context.Response.WriteAsync($"<h1 style='color:red'>{nameof(FirstMiddleware)}——Hello Word Start</h1>");
await _next(context);
await context.Response.WriteAsync($"<h2 style='color:blue'>{nameof(FirstMiddleware)}——Hello Word End</h1>");
}
}
(2)在【Startup.cs】中创建的【Configure】方法中通过以下方法使用中间件:
十六、EF Core
16.1-EF Core可以做什么?
通过实体和数据库的映射,可以通过实体的操作完成对数据的操作。
映射:
- 从数据库到代码视图的映射
- 从代码到数据库的映射
16.2-DB First(数据库优先)
DB First:现有数据库,然后通过映射得到视图(和数据库的表对应);
(1)创建一个控制台应用程序,用来操作并测试。
(2)管理Nuget包,安装对应需要的包。
Install-Package Microsoft.EntityFramewrorkCore
Install-Package Microsoft.Entit3FramewrorkCore.Sqlserver
Install-Package Microsoft.EntityFramewrorkCore.Tools
工具--Nuget包管理器--程序包管理器控制台-命令执行(建议使用这种):
Scaffold-DbContext "Data Source=.;Initial Catalog=CovidAPI;User ID=sa;Password=3344520" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity -Force -Context CovidAPIDbContext -ContextDir /
--可以简写为:
Scaffold-DbContext "Data Source=.;database=CovidAPI;uid=sa;pwd=3344520" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity
命令参数:
-0utputDir 实体文件所存放的文件目录
-ContextDir DbContext文件存放的目录
-Context DbContext文件名
-Schemas 需要生成实体数据的数据表所在的模式
-Tables 等需要生成实体数据的数据表的集合
-DataArnotations
-UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持)
-Force 强制执行,重写已经存在的实体文件
(3)将【EFCore.DbFirst】设置为启动项目,并在【程序包管理器控制台】中选中摸默认项目为【EFCore.DbFirst】,然后执行以上命令:
命令执行完毕,然后会根据数据库中的表生成以下实体和DbContext:
16.3-Code First(代码优先-迁移)
迁移:代码优先,先有代码再有数据库;数据库随着业务变化迁移改变。
迁移命令:
(1)可以通过EFCore代码有的API从代码生成数据库。
(2)可以通过迁移命令生成数据库。
2.1-需要引入程序包
2.2-将【EFCore.CodeFirst】设置为启动项目,并在【程序包管理器控制台】中选中摸默认项目为【EFCore.CodeFirst】,然后执行对应的命令:
然后可以看到生成了迁移文件和快照文件。
16.4-EFCore抓取SQL语句
16.4.1-日志输出
(1)打开Nuget包安装日志输出包。
(2)在数据上下文对象中的【OnConfiguring】方法中进行配置。
16.4.2-SQL Srever Profiler工具
(1)在SQL Srever数据库中【工具】——>【SQL Server Profiler】。
(2)打开之后进行数据库连接,和服务器是同一个服务器。
(3)选择模板和事件。
16.5-EFCore-LinqQuery(LINQ查询)
#region 其他查询
using (CovidAPIDbContext context = new CovidAPIDbContext())
{
//Lambda查询
{
var idlist = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 134, 46, 23, 46, 34 };//in查询
var list = context.Employees.Where(u => idlist.Contains(u.Id));//in查询
foreach (var item in list)
{
Console.WriteLine(item.Name);
}
}
//LINQ查询
{
var list = from u in context.Employees
where new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }.Contains(u.Id)
select u;
foreach (var item in list)
{
Console.WriteLine(item.Name);
}
}
//一般用于分页:Skip、Take——跳过第一条2,取2条
{
//var list = (from u in context.Employees
// where new int[] { 1, 2, 3, 4, 5, 6, 7, 7 }.Contains(u.Id)
// orderby u.Id
// select new
// {
// Name = u.Name,
// Img = u.PictureUrl
// }).Skip(1).Take(2);
//foreach (var item in list)
//{
// Console.WriteLine(item.Name);
//}
}
//多条件
{
var list = context.Employees.Where(u => u.Name.StartsWith("三") && u.Name.EndsWith("三哈"))
.Where(u => u.Name.StartsWith("张"))
.Where(u => u.Name.Length < 5)
.OrderBy(u => u.Id);
foreach (var item in list)
{
Console.WriteLine(item.Name);
}
}
//连接查询
{
var list = (from u in context.Employees
join c in context.Departments
on u.DepartmentId equals c.Id //注意:条件不能写 "==" 等于号,要使用equals
where new int[] { 1, 2, 3, 4, 5, 6, 7 }.Contains(u.Id)
select new
{
Id = u.Id,
Nmae = u.Name,
Bumen = c.Name
}).OrderBy(u => u.Id);
foreach (var item in list)
{
Console.WriteLine($"{item.Id}---{item.Nmae}---{item.Bumen}");
}
}
//左连接
//没有右连接,如果要做右连接,就把顺序调换一下就行了
{
var list = (from u in context.Employees
join c in context.Departments on u.DepartmentId equals c.Id
into ucList
from uc in ucList.DefaultIfEmpty()
where new int[] { 1, 2, 3, 4, 5, 6 }.Contains(u.Id)
select new
{
Id = u.Id,
Nmae = u.Name,
Bumen = uc.Name
}).OrderBy(u => u.Id);
foreach (var item in list)
{
Console.WriteLine($"{item.Id}---{item.Nmae}---{item.Bumen}");
}
}
#endregion
}
16.5-EFCore-执行查询、修改SQL语句
#region 如果遇到非常复杂的查询——建议直接写SQL语句
using (CovidAPIDbContext context = new CovidAPIDbContext()) {
//查询
{
try
{
string sql1 = @"select * from dbo.Employees where Id > @Id";
SqlParameter parameter1 = new SqlParameter("@Id", 4);
var query = context.Employees.FromSqlRaw<Employee>(sql1, parameter1);
foreach (var item in query) {
Console.WriteLine(item.Name);
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
throw;
}
}
//修改
{
string sql2 = @"update dbo.Employees set Name ='修改update' where id=@id";
SqlParameter parameter2 = new SqlParameter("@id", 4);
int flg = context.Database.ExecuteSqlRaw(sql2, parameter2);
}
}
#endregion
16.6-EFCore-State(动态跟踪)
增删改的动作是统一由SavaChanges以后才落实到数据库中去的:
数据库的增删改动作都是统一由SavaChanges之后,统一提交到数据库,是通过状态跟踪,任何一个增删改查的操作都会记录一个状态在内存中,增删改查的状态,一旦SavaChanges,就根据状态落实到数据库中去。
// 摘要:当前这个实体没有被上下文所跟踪
Detached=0,
// 摘要:实体正在被上下文跟踪,并且存在于数据库中,他的数据库中的数据没有被更改。
Unchanged=1,
// 摘要:实体被上下文跟踪,并且存在数据库中,并已标记从数据库中删除,并还没有删除。
Deleted=2,
// 摘要:实体被上下文跟踪,部分或他的所有属性都已经被修改。
Modified=3,
// 摘要:实体被上下文跟踪,但在数据库中还不存在。
Added=4
注意:
状态跟踪实现了增删改便捷,但是也会有性能消耗;因为每次都要去和内存的副本的状态去做比较的。
16.7-EFCore-事务(调优小技巧)
SavaChanges就是保证事务的,多个对于数据库的操作,统一SavaChanges,就是开启了一个事务的。
EF Core自带的两个事务:
(1)SavaChanges
(2)IDbContextTransaction
16.8-EFCore-调优小技巧
(1)尽量不要进行Tolist();
(2)尽量不要使用FirstOrDefault,要使用Find进行查询。
(3)只做查询时,不需要进行增删改,就去掉状态跟踪。
全局取消
十七、EFCore整合-分层架构
17.1-分层架构
-
没有分层的缺点:
- 职责不清晰
- 如果有一处修改,可能会导致需要重新修改,需要重新测试。
-
分层的优点:
- 职责更清晰
- 需求的变更不同修改全部代码
- 人员更好调配——让更专业的人做专业的事。
-
三层架构:
-
UI:展示给用户,视图层
-
BLL:业务逻辑层
-
DAL:数据访问层
分层以后:要求不能跨层调用,UI层——BLL层——DAL层
上一章节:https://www.cnblogs.com/kimiliucn/p/17620153.html
原文地址:https://www.cnblogs.com/kimiliucn/p/17620159.html
版权声明:本文为原创文章,版权归 [西瓜程序猿] 所有,转载请注明出处,有任何疑问请私信咨询。
原文链接:https://www.cnblogs.com/kimiliucn/p/17620159.html