NewGaoKaoApi/.docs/contents/guide/cheat-sheet.md

581 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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` 中,配置登录授权:
<img src="http://apk.neters.club/images/20200507213830.png" alt="swagger" width="600" >
## 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` 根据数据库(支持多库),生成多层代码,算是简单代码生成器;
其他功能,[核心功能与进度](http://apk.neters.club/.doc/guide/#%E5%8A%9F%E8%83%BD%E4%B8%8E%E8%BF%9B%E5%BA%A6)
## 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` ,可以参考 [#如何项目重命名](http://apk.neters.club/.doc/guide/getting-started.html#%E5%A6%82%E4%BD%95%E9%A1%B9%E7%9B%AE%E9%87%8D%E5%91%BD%E5%90%8D)
同时,你也可以再 `Nuget` 管理器中,搜索到:
<img src="http://apk.neters.club/images/20200507223058.png" alt="nuget" width="600" >
## 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>() { };
}
}
```