20 KiB
Z 主要知识点
AOP
本项目多处采用面向切面编程思想——AOP,除了广义上的过滤器和中间件以外,主要通过动态代理的形式来实现AOP编程思想,主要的案例共有四个,分别是:
1、服务日志AOP;
2、服务InMemory缓存AOP;
3、服务Redis缓存AOP;
4、服务事务AOP;
具体的代码可以在 New_College\New_College\AOP 文件夹下查看。
与此同时,多个AOP也设置了阀门来控制是否开启,具体的可以查看 appsettings.json 中的:
"AppSettings": {
"RedisCachingAOP": {
"Enabled": false,
"ConnectionString": "127.0.0.1:6319"
},
"MemoryCachingAOP": {
"Enabled": true
},
"LogAOP": {
"Enabled": false
},
"TranAOP": {
"Enabled": false
},
"SqlAOP": {
"Enabled": false
}
},
Appsettings
整个系统通过一个封装的操作类 Appsettings.cs 来控制配置文件 appsettings.json 文件,
操作类地址在:\New_College.Common\Helper 文件夹下。
具体的使用方法是:
Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" })
// 里边的参数,按照 appsettings.json 中设置的层级顺序来写,可以获取到指定的任意内容。
AspNetCoreRateLimit
系统使用 AspNetCoreRateLimit 组件来实现ip限流:
1、添加 nuget 包:
<PackageReference Include="AspNetCoreRateLimit" Version="3.0.5" />
2、注入服务 IpPolicyRateLimitSetup.cs
services.AddIpPolicyRateLimitSetup(Configuration);
3、配置中间件
// Ip限流,尽量放管道外层
app.UseIpRateLimiting();
4、配置数据
具体的内容,自行百度即可
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,//返回状态码
"GeneralRules": [//规则,结尾一定要带*
{
"Endpoint": "*",
"Period": "1m",
"Limit": 120
},
{
"Endpoint": "*:/api/blog*",
"Period": "1m",
"Limit": 30
}
]
}
Async-Await
整个系统采用 async/await 异步编程,符合主流的开发模式,
特别是对多线程开发很友好。
Authorization-Ids4
本系统 v2.0 版本(目前的系统已经集成 ids4 和 jwt,并且可以自由切换),已经支持了统一授权认证,和 blog 项目、Admin 项目、DDD 项目等一起,使用一个统一的认证中心。
具体的代码参考:.\New_College\Extensions 文件夹下的 Authorization_Ids4Setup.cs ,注意需要引用指定的 nuget 包,核心代码如下:
//【认证】
services.AddAuthentication(o =>
{
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = nameof(ApiResponseHandler);
o.DefaultForbidScheme = nameof(ApiResponseHandler);
})
// 2.添加Identityserver4认证
.AddIdentityServerAuthentication(options =>
{
options.Authority = Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" });
options.RequireHttpsMetadata = false;
options.ApiName = Appsettings.app(new string[] { "Startup", "IdentityServer4", "ApiName" });
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = "api_secret";
})
如何在Swagger中配置Ids4?
很简单,直接在 SwaggerSetup.cs 中直接接入 oauth、Implicit 即可:
//接入identityserver4
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" })}/connect/authorize"),
Scopes = new Dictionary<string, string> {
{
"new_college.api","ApiResource id"
}
}
}
}
});
然后在 IdentityServer4 项目中,做指定的修改,配置 8081 的回调地址:
new Client {
ClientId = "blogadminjs",
ClientName = "Blog.Admin JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris =
{
"http://vueadmin.neters.club/callback",
// 这里要配置回调地址
"http://localhost:8081/oauth2-redirect.html"
},
PostLogoutRedirectUris = { "http://vueadmin.neters.club" },
AllowedCorsOrigins = { "http://vueadmin.neters.club" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"roles",
"new_college.api"
}
},
然后再 Swagger 中,配置登录授权:
Authorization-JWT
如果你不想使用 IdentityServer4 的话,也可以使用 JWT 认证,同样是是New_College\New_College\Extensions 文件夹下的 AuthorizationSetup.cs 中有关认证的部分:
1.添加JwtBearer认证服务
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
})
AutoMapper
使用 AutoMapper 组件来实现 Dto 模型的传输转换,具体的用法,可以查看:
New_College\New_College\Extensions 文件夹下的 AutoMapperSetup.cs 扩展类,
通过引用 AutoMapper 和 AutoMapper.Extensions.Microsoft.DependencyInjection 两个 nuget 包,并设置指定的 profile 文件,来实现模型转换控制。
// 比如如何定义:
public class CustomProfile : Profile
{
/// <summary>
/// 配置构造函数,用来创建关系映射
/// </summary>
public CustomProfile()
{
CreateMap<BlogArticle, BlogViewModels>();
CreateMap<BlogViewModels, BlogArticle>();
}
}
// 比如如何使用
models = _mapper.Map<BlogViewModels>(blogArticle);
具体的查看项目中代码即可。
CORS
在线项目使用的是 nginx 跨域代理,但是同时也是支持 CORS 代理:
1、注入服务 services.AddCorsSetup(); 具体代码 New_College\New_College\Extensions 文件夹下的 CorsSetup.cs 扩展类;
2、配置中间件 app.UseCors("LimitRequests"); ,要注意中间件顺序;
3、配置自己项目的前端端口,通过在 appsettings.json 文件中配置自己的前端项目 ip:端口 ,来实现跨域:
"Startup": {
"Cors": {
"IPs": "http://127.0.0.1:2364,http://localhost:2364,http://localhost:8080,http://localhost:8021,http://localhost:1818"
}
},
DI-AutoFac
项目使用了依赖注入,除了原生的依赖注入以外,更多的使用的是第三方组件 Autofac :
1、引用依赖包:
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="5.0.1" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="4.5.0" />
主要是第一个 nuget 包,下边的是为了实现动态代理 AOP 操作;
2、项目之间采用引用解耦的方式,通过反射来注入服务层和仓储层的程序集 dll 来实现批量注入,更方便,以后每次新增和修改 Service 层和 Repository 层,只需要 F6 编译一下即可,具体代码查看 Startup.cs:
// 注意在CreateDefaultBuilder中,添加Autofac服务工厂
public void ConfigureContainer(ContainerBuilder builder)
{
var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;
//builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();
#region 带有接口层的服务注入
var servicesDllFile = Path.Combine(basePath, "New_College.Services.dll");
var repositoryDllFile = Path.Combine(basePath, "New_College.Repository.dll");
if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
{
throw new Exception("Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。");
}
// AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
var cacheType = new List<Type>();
if (Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool())
{
builder.RegisterType<BlogRedisCacheAOP>();
cacheType.Add(typeof(BlogRedisCacheAOP));
}
if (Appsettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool())
{
builder.RegisterType<BlogCacheAOP>();
cacheType.Add(typeof(BlogCacheAOP));
}
if (Appsettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool())
{
builder.RegisterType<BlogTranAOP>();
cacheType.Add(typeof(BlogTranAOP));
}
if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool())
{
builder.RegisterType<BlogLogAOP>();
cacheType.Add(typeof(BlogLogAOP));
}
// 获取 Service.dll 程序集服务,并注册
var assemblysServices = Assembly.LoadFrom(servicesDllFile);
builder.RegisterAssemblyTypes(assemblysServices)
.AsImplementedInterfaces()
.InstancePerDependency()
.EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
.InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。
// 获取 Repository.dll 程序集服务,并注册
var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
builder.RegisterAssemblyTypes(assemblysRepository)
.AsImplementedInterfaces()
.InstancePerDependency();
#endregion
#region 没有接口层的服务层注入
//因为没有接口层,所以不能实现解耦,只能用 Load 方法。
//注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法
//var assemblysServicesNoInterfaces = Assembly.Load("New_College.Services");
//builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces);
#endregion
#region 没有接口的单独类 class 注入
//只能注入该类中的虚方法
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
.EnableClassInterceptors()
.InterceptedBy(cacheType.ToArray());
#endregion
// 这里和注入没关系,只是获取注册列表,请忽略
tsDIAutofac.AddRange(assemblysServices.GetTypes().ToList());
tsDIAutofac.AddRange(assemblysRepository.GetTypes().ToList());
}
3、然后 Program.cs 中也要加一句话:.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS
DI-NetCore
除了主要的 Autofac 依赖注入以外,也减少的使用了原生的依赖注入方式,很简单,比如这样的:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 注入权限处理器
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
services.AddSingleton(permissionRequirement);
Filter
项目中一共有四个过滤器
1、GlobalAuthorizeFilter.cs —— 全局授权配置,添加后,就可以不用在每一个控制器上添加 [Authorize] 特性,但是3.1版本好像有些问题,【暂时放弃使用】;
2、GlobalExceptionFilter.cs —— 全局异常处理,实现 actionContext 级别的异常日志收集;
3、GlobalRoutePrefixFilter.cs —— 全局路由前缀公约,统计在路由上加上前缀;
4、UseServiceDIAttribute.cs —— 测试注入,【暂时无用】;
文件地址在 .\New_College\Filter 文件夹下,其中核心的是 2 个,重点使用的是 1 个 —— 全局异常错误日志 GlobalExceptionsFilter:
通过注册在 MVC 服务 services.AddControllers() 中,实现全局异常过滤:
services.AddControllers(o =>
{
// 全局异常过滤
o.Filters.Add(typeof(GlobalExceptionsFilter));
// 全局路由权限公约
//o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention());
// 全局路由前缀,统一修改路由
o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name)));
})
Framework
项目采用 服务+仓储+接口 的多层结构,使用依赖注入,并且通过解耦项目,较完整的实现了 DIP 原则:
高层模块不应该依赖于底层模块,二者都应该依赖于抽象。
抽象不应该依赖于细节,细节应该依赖于抽象。
同时项目也封装了:
CodeFirst 初始化数据库以及数据;
DbFirst 根据数据库(支持多库),生成多层代码,算是简单代码生成器;
其他功能,核心功能与进度
Log
通过集成 Log4Net 组件,完美配合 NetCore 官方的 ILogger<T> 接口,实现对日志的管控,引用 nuget 包 Microsoft.Extensions.Logging.Log4Net.AspNetCore:
Program.cs
webBuilder
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, builder) =>
{
//该方法需要引入Microsoft.Extensions.Logging名称空间
builder.AddFilter("System", LogLevel.Error); //过滤掉系统默认的一些日志
builder.AddFilter("Microsoft", LogLevel.Error);//过滤掉系统默认的一些日志
//添加Log4Net
//var path = Directory.GetCurrentDirectory() + "\\log4net.config";
//不带参数:表示log4net.config的配置文件就在应用程序根目录下,也可以指定配置文件的路径
//需要添加nuget包:Microsoft.Extensions.Logging.Log4Net.AspNetCore
builder.AddLog4Net();
});
然后直接在需要的地方注入使用,比如在控制器中
public UserController(ILogger<UserController> logger)
然后就可以使用了。
注意:日志 其实是分为两部分的:
netcore输出(控制台、输出窗口等) 和ILogger持久化
两者对应配置也不一样,就比如上边的过滤,是针对日志持久化的,如果想要对控制台进行控制,需要配置appsettings.json中的Logging节点
MemoryCache
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
Middleware
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
MiniProfiler
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
publish
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
Redis
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
Repository
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
SeedData
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
SignalR
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
SqlSugar
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
SqlSugar-Codefirst&DataSeed
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
SqlSugar-SqlAOP
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
Swagger
精力有限,还是更新中...
如果你愿意帮忙,可以直接在GitHub中,提交pull request,
我会在后边的贡献者页面里,列出你的名字和项目地址做推广
T4
项目集成 T4 模板 .\New_College.FrameWork 层,目的是可以一键生成项目模板代码。
1、需要在 DbHelper.ttinclude 中配置连接数据库连接字符串;
2、针对每一层的代码,就去指定的 .tt 模板,直接 CTRL+S 保存即可;
注意,目前的代码是
SqlServer版本的,其他数据库版本的,可以去群文件查看。
Test-xUnit
项目简单使用了单元测试,通过 xUnit 组件,具体的可以查看 New_College.Tests 层相关代码。
目前单元测试用例还比较少,大家可以自行添加。
Temple-Nuget
本项目封装了 Nuget 自定义模板,你可以根据这个模板,一键创建自己的项目名,具体的操作,可以双击项目根目录下的 CreateYourProject.bat ,可以参考 #如何项目重命名
同时,你也可以再 Nuget 管理器中,搜索到:

UserInfo
项目中封装了获取用户信息的代码:
在 .\New_College.Common\HttpContextUser 文件夹下 AspNetUser.cs 实现类和 IUser.cs 接口。
如果使用,首先需要注册相应的服务,参见:.\New_College\Extensions 文件夹下的 HttpContextSetup.cs;
然后,就直接在控制器构造函数中,注入接口 IUser 即可;
注意:
1、如果要想获取指定的服务,必须登录,也就是必须要在Header中传递有效Token,这是肯定的。
2、如果要获取用户信息,一定要在中间件app.UseAuthentication()之后(不要问为什么),控制器肯定在它之后,所以能获取到;
3、【并不是】一定需要添加[Authorize]特性,如果你加了这个特性,可以直接获取,但是如果不加,可以从我的AspNetUser.cs方法中,有一个直接从Header中解析的方法List<string> GetUserInfoFromToken(string ClaimType);:
public string GetToken()
{
return _accessor.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "");
}
public List<string> GetUserInfoFromToken(string ClaimType)
{
var jwtHandler = new JwtSecurityTokenHandler();
if (!string.IsNullOrEmpty(GetToken()))
{
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(GetToken());
return (from item in jwtToken.Claims
where item.Type == ClaimType
select item.Value).ToList();
}
else
{
return new List<string>() { };
}
}