前言:前段时间根据 [老张的哲学] 大佬讲解的视频做的笔记,讲的很不错。此文主要记录JWT/DI依赖注入/AOP面向切面编程/DTO/解决跨域等相关知识,还包含一些.NET Core项目实战的一些案例。我是西瓜程序猿,感谢大家的支持!
目录
一、ASP.NET Core基础
1.1-.NET Core概述
1.1.1-.NET Croe简介
1.1.2-进程内托管 和 进程外托管
1.2-JWT详解
1.2.1-为什么要保护API?
1.2.2-设计原则
1.2.3-加密算法
1.2.4-有哪些方式保护方式
1.2.5-JWT的好处?
1.2.6-如何使用JWT?
1.3-DI依赖注入(IOC的思想)
1.3.1-相关基本概念
1.3.2-常见的依赖注入有哪些?
1.3.3-三种注入方法
1.3.4-三种声明周期-注册要保持一致
1.3.5-为什么要使用依赖注入?
1.3.6-依赖注入步骤
1.4-AOP面向切面编程(思想)
1.4.1-什么是AOP?
1.4.2-AOP的特点?
1.4.3-思想的来源
1.4.4-AOP只是流程
1.4.5-AOP应用场景(不是业务逻辑的逻辑、是公共逻辑)
1.4.6-多种思想应用的区别
1.4.7-AOP有哪些优势?
1.4.8-AOP的使用
1.5-DTO与多模型
1.5.1-什么是DTO?
1.5.2-为什么要使用数据传输对象DTO?
1.5.3-DTO和ViewModel(视图模型)是一回事么?
1.5.5-多种模型概论
1.5.6-如何使用DTO?
1.6-跨域
1.6.1-跨域的相关概念
1.6.2-JSONP
1.6.3-Proxy--接口级别
1.6.4-Cors--项目级别
1.6.5-Nginx
1.6.6-Socket--非HTTP请求
1.6.7-其他跨域操作
二、ASP.NET Core项目实战案例
2.1-Autofac依赖注入
2.2-AutoMapper对象映射
2.3-IP Limit限流解析
2.4-CORS跨域与"钓鱼"
2.5-Swagger接口文文档
2.6-MiniProfiler性能分析
2.7-StackEx.Redis安装与使用
(1)为什么要学习.NET Core?
.NET Core是为了重新启动某些Framework组件而为其他人提供平台工作的机会,由于.NET Framework主要以委托(C#)代码位基础构建了因此这些部分不需要改代码即可移至.NET Core。
(2).NET Core运用的多吗?
微信支付、网易游戏、三星电子、Adobe、Stackoverflow等
(3)什么是.NET Core?
跨平台、自托管、开源、高性能,.NETCore是基于Kestrel执行的控制台程序。
(4)中间件的执行过程?
(5)中间件3种写法?
数据传输对象(DTO全称为Data Transfer Object):是一种设计模式之间传输数据的软件应用系统。数据传输对象往往是数据访问对象从数据库检索数据。数据传输对象与数据交互对象或数据访问对象之间的差异是一个以下不具有任何行为除了存储和检索的数据(范文和存取器)。
域名组成:
(1)西瓜程序猿创建的项目结构如下:
项目相关依赖如下:
(2)在【Autofac依赖注入】层导入相关Nuget包:
(3)在Program.cs使用Autofac工厂:
(4)在AutofacModuleRegister写入以下代码,继承自Modele,并重写Load方法。
protected override void Load(ContainerBuilder builder)
{
var basePath = AppContext.BaseDirectory;
#region 带有接口层的服务注入
var servicesDllFile = Path.Combine(basePath, "Autofac.Service.dll");
var repositoryDllFile = Path.Combine(basePath, "Autofac.Repository.dll");
if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
{
var msg = "Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。";
throw new Exception(msg);
}
// 获取 Service.dll 程序集服务,并注册
var assemblysServices = Assembly.LoadFrom(servicesDllFile);
builder.RegisterAssemblyTypes(assemblysServices)
.AsImplementedInterfaces()
.InstancePerDependency();
// 获取 Repository.dll 程序集服务,并注册
var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
builder.RegisterAssemblyTypes(assemblysRepository)
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerDependency();
#endregion
}
(5)在控制器中使用构造函数依赖注入。
(6)然后编译项目,最后运行项目试试,可以发现报错了,因为Repository.dll和service.dll 丢失,因为项目解耦了。解决方案如下:
将这下面2个实现层输出位置如下:
(1)引入包。
(2)创建对应的数据实体,和要返回的视图模型实体。
实体:
视图模型实体:
(3)创建一个【CustomProfile】类,需要继承自【Profile】,用来创建关系映射。
(4)创建一个【AutoMapperConfig】类,用来静态全局 AutoMapper 配置文件。
(5)创建一个【AutoMapperSetup】类,用于Automapper的启动服务。
(6)然后在【Startup.cs】的ConfigreServices方法注册相关服务。
2.3.1-什么是限流?
2.3.2-时间窗口算法
2.3.3-漏斗算法
2.3.4-令牌算法
2.3.5-时间窗口(代码实现)
(1)安装Nuget包。
(2)创建【IpPolicyRateLimitSetup】类,用于限流,启用服务。
(3)在【Startup.cs】的ConfigreServices方法注册相关服务。
(4)在【appsettings.json】中根节点写入一下配置。
"IpRateLimiting": {
"EnableEndpointRateLimiting": true, //false: 全局执行API——true:针对每个API
"StackBlockedRequests": false, //False:应在另一个计数器上记录拒绝次数
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"IpWhitelist": [], //添加白名单
"EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ],//添加API的白名单
"ClientWhitelist": [ "dev-client-1", "dev-client-2" ],//添加客户端的白名单
"QuotaExceededResponse": {
"Content": "{{\"status\":429,\"msg\":\"时间限流:访问过于频繁,请稍后重试\",\"success\":false}}",
"ContentType": "application/json",
"StatusCode": 429
},
"HttpStatusCode": 429, //返回状态码
"GeneralRules": [ //api规则,结尾一定要带*
{
//针对blog的API,1分钟最多访问20次
"Endpoint": "*:/api/blog*", //规则
"Period": "1m", //时间
"Limit": 20 //次数
},
{
//无论什么API,1秒最能请求3次
"Endpoint": "*/api/*",
"Period": "1s",
"Limit": 3
},
{
//无论什么API,1分钟最能请求30次
"Endpoint": "*/api/*",
"Period": "1m",
"Limit": 30
},
{
//无论什么API,12小时秒最能请求50次
"Endpoint": "*/api/*",
"Period": "12h",
"Limit": 500
}
]
}
(5)写一个中间件,用于IP限流。
(6)在【Startup.cs】的Configure中配置中间件。
(7)平凡请求接口时。
2.4.1-没有跨域会导致什么问题?
2.4.2-CORS跨域代码实现
(1)安装Nuget包。
(2)创建【CorsSetup】类,用于限流,启用服务。
(3)在【Startup.cs】的ConfigreServices方法注册相关服务。
(4)在【appsettings.json】中根节点写入一下配置。
"Cors": {
"PolicyName": "CorsIpAccess", //策略名称
"EnableAllIPs": true, //当为true时,开放所有IP均可访问。
// 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的
// 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的
"IPs": "http://127.0.0.1:2364,http://localhost:2364"
},
(5)在【Startup.cs】的Configure中配置中间件。
// CORS跨域
app.UseCors(Appsettings.app(new string[] { "Startup", "Cors", "PolicyName" }));
(1)导入Nuget包。
(2)创建【SwaggerSetup】类,启动Swagger服务。
///
/// Swagger 启动服务
///
public static class SwaggerSetup
{
private static readonly ILog log =LogManager.GetLogger(typeof(SwaggerSetup));
public static void AddSwaggerSetup(this IServiceCollection services)
{
if (services == null)
throw new ArgumentNullException(nameof(services));
//获取项目的根路径
var basePath = AppContext.BaseDirectory;
//获取项目名
var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" });
services.AddSwaggerGen(c =>
{
//遍历出全部的版本,做文档信息展示
typeof(ApiVersions).GetEnumNames().ToList().ForEach(version =>
{
c.SwaggerDoc(version, new OpenApiInfo
{
Version = version,
//RuntimeInformation.FrameworkDescription:运行时版本
Title = $"{ApiName} 西瓜程序猿 - 接口文档——{RuntimeInformation.FrameworkDescription}",
Description = $"{ApiName} HTTP API " + version,
//Contact:联系
//License:声明
});
c.OrderActionsBy(o => o.RelativePath);//接口排序
});
try
{
//这个就是刚刚配置的xml文件名【文档API的注释】
var xmlPath = Path.Combine(basePath, "Blog.Core.xml");
//默认的第二个参数是false,这个是controller的注释,记得修改
c.IncludeXmlComments(xmlPath, true);
//这个就是Model层的xml文件名【Model相关的注释】
var xmlModelPath = Path.Combine(basePath, "Blog.Core.Model.xml");
c.IncludeXmlComments(xmlModelPath);
}
catch (Exception ex)
{
log.Error("Blog.Core.xml和Blog.Core.Model.xml 丢失,请检查并拷贝。\n" + ex.Message);
}
// 开启加权小锁
c.OperationFilter();
c.OperationFilter();
// 在header中添加token,传递到后台
c.OperationFilter();
// ids4和jwt切换
if (Permissions.IsUseIds4)
{
//接入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 {
{
"blog.core.api","ApiResource id"
}
}
}
}
});
}
else
{
// Jwt Bearer 认证,必须是 oauth2
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Description = "描述:西瓜程序猿 - JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.ApiKey
});
}
});
}
}
///
/// 自定义版本
///
public class CustomApiVersion
{
///
/// Api接口版本 自定义
///
public enum ApiVersions
{
///
/// V1 版本
///
V1 = 1,
///
/// V2 版本
///
V2 = 2,
}
}
(3)在【Startup.cs】的ConfigreServices方法注册相关服务。
(4)创建一个【UseSwaggerMildd】类,开启并处理Swagger中间件。
private static readonly ILog log = LogManager.GetLogger(typeof(SwaggerMildd));
public static void UseSwaggerMildd(this IApplicationBuilder app, Func streamHtml)
{
if (app == null) throw new ArgumentNullException(nameof(app));
app.UseSwagger();
app.UseSwaggerUI(c =>
{
//根据版本名称倒序 遍历展示
var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" });
typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version =>
{
c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");
});
c.SwaggerEndpoint($"https://petstore.swagger.io/v2/swagger.json", $"{ApiName} pet");
// 将swagger首页,设置成我们自定义的页面,记得这个字符串的写法:{项目名.index.html}
if (streamHtml.Invoke() == null)
{
var msg = "index.html的属性,必须设置为嵌入的资源";
log.Error(msg);
throw new Exception(msg);
}
c.IndexStream = streamHtml;
if (Permissions.IsUseIds4)
{
c.OAuthClientId("blogadminjs");
}
// 路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.RoutePrefix = "doc";
c.RoutePrefix = "";
});
}
(5)在【Startup.cs】中配置中间件,注意顺序。
(6)index.html记得设置为【嵌套的资源】
(1)导入Nuget包。
(2)创建【AddMiniProfilerSetup】类,启动Swagger服务。
public static void AddMiniProfilerSetup(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
if(Appsettings.app(new string[] { "Startup", "MiniProfiler", "Enabled" }).ObjToBool())
{
services.AddMiniProfiler();
}
// 3.x使用MiniProfiler,必须要注册MemoryCache服务
// services.AddMiniProfiler(options =>
// {
// options.RouteBasePath = "/profiler";
// //(options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10);
// options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.Left;
// options.PopupShowTimeWithChildren = true;
// // 可以增加权限
// //options.ResultsAuthorize = request => request.HttpContext.User.IsInRole("Admin");
// //options.UserIdProvider = request => request.HttpContext.User.Identity.Name;
// }
//);
}
(3)在【Startup.cs】的ConfigreServices方法注册相关服务。
(4)创建一个【UseMiniProfilerMildd】类,开启并处理Swagger中间件。
private static readonly ILog log = LogManager.GetLogger(typeof(MiniProfilerMildd));
public static void UseMiniProfilerMildd(this IApplicationBuilder app)
{
if (app == null) throw new ArgumentNullException(nameof(app));
try
{
if (Appsettings.app("Startup", "MiniProfiler", "Enabled").ObjToBool())
{
// 性能分析
app.UseMiniProfiler();
}
}
catch (Exception e)
{
log.Error($"An error was reported when starting the MiniProfilerMildd.\n{e.Message}");
throw;
}
}
(5)在【Startup.cs】中配置中间件,注意顺序。
(1)导入Nuget包。
(2)创建【RedisCacheSetup】类,启动Redis服务。
(3)在【Startup.cs】的ConfigreServices方法注册相关服务。
(4)创建一个名为【IRedisCacheManager】接口,用于Redis缓存。
///
/// Redis缓存接口
///
public interface IRedisCacheManager
{
//获取 Reids 缓存值
string GetValue(string key);
//获取值,并序列化
TEntity Get(string key);
//保存
void Set(string key, object value, TimeSpan cacheTime);
//判断是否存在
bool Get(string key);
//移除某一个缓存值
void Remove(string key);
//全部清除
void Clear();
}
(5)创建一个【RedisCacheManager】类,并实现【IRedisCacheManager】接口
public class RedisCacheManager : IRedisCacheManager
{
private readonly string redisConnenctionString;
public volatile ConnectionMultiplexer redisConnection;
private readonly object redisConnectionLock = new object();
public RedisCacheManager()
{
string redisConfiguration = Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "ConnectionString" });//获取连接字符串
if (string.IsNullOrWhiteSpace(redisConfiguration))
{
throw new ArgumentException("Redis配置为空", nameof(redisConfiguration));
}
this.redisConnenctionString = redisConfiguration;
this.redisConnection = GetRedisConnection();
}
///
/// 核心代码,获取连接实例
/// 通过双if 夹lock的方式,实现单例模式
///
///
private ConnectionMultiplexer GetRedisConnection()
{
//如果已经连接实例,直接返回
if (this.redisConnection != null && this.redisConnection.IsConnected)
{
return this.redisConnection;
}
//加锁,防止异步编程中,出现单例无效的问题
lock (redisConnectionLock)
{
if (this.redisConnection != null)
{
//释放redis连接
this.redisConnection.Dispose();
}
try
{
var config = new ConfigurationOptions
{
AbortOnConnectFail = false,
AllowAdmin = true,
ConnectTimeout = 15000,//改成15s
SyncTimeout = 5000,
//Password = "Pwd",//Redis数据库密码
EndPoints = { redisConnenctionString }// connectionString 为IP:Port 如”192.168.2.110:6379”
};
this.redisConnection = ConnectionMultiplexer.Connect(config);
}
catch (Exception)
{
throw new Exception("Redis服务未启用,请开启该服务,并且请注意端口号,本项目使用的的6319,而且我的是没有设置密码。");
}
}
return this.redisConnection;
}
///
/// 清除
///
public void Clear()
{
foreach (var endPoint in this.GetRedisConnection().GetEndPoints())
{
var server = this.GetRedisConnection().GetServer(endPoint);
foreach (var key in server.Keys())
{
redisConnection.GetDatabase().KeyDelete(key);
}
}
}
///
/// 判断是否存在
///
///
///
public bool Get(string key)
{
return redisConnection.GetDatabase().KeyExists(key);
}
///
/// 查询
///
///
///
public string GetValue(string key)
{
return redisConnection.GetDatabase().StringGet(key);
}
///
/// 获取
///
///
///
///
public TEntity Get(string key)
{
var value = redisConnection.GetDatabase().StringGet(key);
if (value.HasValue)
{
//需要用的反序列化,将Redis存储的Byte[],进行反序列化
return SerializeHelper.Deserialize(value);
}
else
{
return default(TEntity);
}
}
///
/// 移除
///
///
public void Remove(string key)
{
redisConnection.GetDatabase().KeyDelete(key);
}
///
/// 设置
///
///
///
///
public void Set(string key, object value, TimeSpan cacheTime)
{
if (value != null)
{
//序列化,将object值生成RedisValue
redisConnection.GetDatabase().StringSet(key, SerializeHelper.Serialize(value), cacheTime);
}
}
///
/// 增加/修改
///
///
///
///
public bool SetValue(string key, byte[] value)
{
return redisConnection.GetDatabase().StringSet(key, value, TimeSpan.FromSeconds(120));
}
}
(6)修改【appsetting.json】配置文件。
(4)创建一个【UseSwaggerMildd】类,开启并处理Swagger中间件。
private static readonly ILog log = LogManager.GetLogger(typeof(SwaggerMildd));
public static void UseSwaggerMildd(this IApplicationBuilder app, Func streamHtml)
{
if (app == null) throw new ArgumentNullException(nameof(app));
app.UseSwagger();
app.UseSwaggerUI(c =>
{
//根据版本名称倒序 遍历展示
var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" });
typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version =>
{
c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");
});
c.SwaggerEndpoint($"https://petstore.swagger.io/v2/swagger.json", $"{ApiName} pet");
// 将swagger首页,设置成我们自定义的页面,记得这个字符串的写法:{项目名.index.html}
if (streamHtml.Invoke() == null)
{
var msg = "index.html的属性,必须设置为嵌入的资源";
log.Error(msg);
throw new Exception(msg);
}
c.IndexStream = streamHtml;
if (Permissions.IsUseIds4)
{
c.OAuthClientId("blogadminjs");
}
// 路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.RoutePrefix = "doc";
c.RoutePrefix = "";
});
}
(5)在【Startup.cs】中配置中间件,注意顺序。
版权声明:本文为原创文章,版权归 [西瓜程序猿] 所有,转载请注明出处,有任何疑问请私信咨询。
原文链接:.NET Core基础到实战案例零碎学习笔记_西瓜程序猿的博客-CSDN博客