导图社区 .net core程序员技术栈
.net core程序员技术栈, 常用技术知识点, 备忘记录
编辑于2020-07-15 19:16:07暂无相关模板推荐
解决方案
.net core
过滤器
过滤器执行顺序
``` Authorization filters 授权筛选器 Resource filters 资源筛选器 Action filters 操作筛选器 (Razor Pages 中使用 IPageFilter 和IAsyncPageFilter) Exception filters 异常筛选器 Result filters 结果筛选器 ``` 1. Authorize和IAsyncAuthorizationFilter 2. ControllerBase OnActionExecuting. 3. CustomActionFilter OnActionExecuting. 4. Action的本体执行 5. CustomActionFilter OnActionExecuted. 6. ControllerBase OnActionExecuted. 7. ResultFilter in. ResultFilter out. 验证首先执行. 其次父Controller的OnActionExecuting 然后普通过滤器的OnActionExecuting Action自身 普通过滤器的OnActionExecuted 父Controller的OnActionExecuted 最后是ResultFilter的Executing和Executed
例子1,异步的授权过滤器
public class AppForbidFilter : Attribute, IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var appName = context.RouteData.Values["app"]?.ToString(); if (!string.IsNullOrWhiteSpace(appName)) { var _redisConn = (IConnectionMultiplexer)context.HttpContext.RequestServices.GetService(typeof(IConnectionMultiplexer)); var _database = _redisConn.GetDatabase(2); var forbidAppKey = $"forbid-{appName}"; var forbidValue = await _database.StringGetAsync(forbidAppKey); //var forbidValue = _database.StringGet(forbidAppKey); if (forbidValue.HasValue) { context.Result = new ObjectResult(new { Message = "app 被禁止访问" }) { StatusCode = 403 }; }else await next(); }else await next(); } }
例子2,异步的常规过滤器
public class AppApiFilter : IAsyncActionFilter { private readonly IAppProvider _appProvider; /// <summary> /// Initializes a new instance of the <see cref="T:Schema.API.Pipeline.AppApiFilter"/> class. /// </summary> /// <param name="appProvider">App provider.</param> public AppApiFilter(IAppProvider appProvider) { _appProvider = appProvider; } /// <summary> /// Ons the action execution async. /// </summary> /// <returns>The action execution async.</returns> /// <param name="context">Context.</param> /// <param name="next">Next.</param> public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var appName = context.RouteData.Values["app"]?.ToString(); if (!string.IsNullOrWhiteSpace(appName)) { IAppEntity app = null; Guid guid = Guid.Empty; // 兼容App参数传入AppGuid和Name if (Guid.TryParse(appName, out guid)) { app = await _appProvider.FindAppByIdAsync(guid); } else { app = await _appProvider.FindAppByNameAsync(appName); } if (app == null) { context.Result = new NotFoundResult(); return; } context.HttpContext.Features.Set<IAppFeature>(new AppFeature(app)); } await next(); } }
常见的内置过滤器
ActionFilterAttribute ExceptionFilterAttribute ResultFilterAttribute FormatFilterAttribute ServiceFilterAttribute TypeFilterAttribute
全局过滤器
Startup添加对应配置: public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.Filters.Add(typeof(WebApiResultMiddleware)); options.RespectBrowserAcceptHeader = true; }); }
例子1, webapi返回统一的格式
public class WebApiResultFilter : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { //根据实际需求进行具体实现 if (context.Result is ObjectResult) { var objectResult = context.Result as ObjectResult; if (objectResult.Value == null) { context.Result = new ObjectResult(new { code = 404, sub_msg = "未找到资源", msg = "" }); } else { context.Result = new ObjectResult(new { code = 200, msg = "", result = objectResult.Value }); } } else if (context.Result is EmptyResult) { context.Result = new ObjectResult(new { code = 404, sub_msg = "未找到资源", msg = "" }); } else if (context.Result is ContentResult) { context.Result = new ObjectResult(new { code = 200, msg = "", result= (context.Result as ContentResult).Content }); } else if (context.Result is StatusCodeResult) { context.Result = new ObjectResult(new { code = (context.Result as StatusCodeResult).StatusCode, sub_msg = "", msg = "" }); } } }
session和cookie
参考文章: https://www.cnblogs.com/justdojava/p /11212028.html cookie和session逐级深入讲解
每次请求, 系统都会带上cookie
sessionId通过cookie存储在客户端
分布式session(存储在redis)
IUnitOfWork
缓存
OutPutCache
ILock实现Redis分布式缓存
Endpoint路由
解耦了路由和Action, 可以提前获取Action的Attribute
启动顺序
1. ConfigureWebHostDefault, 注册配置的组件, 容器的组件
2.configureHostConfiguration, 配置app启动的配置, 比如端口, url地址
3. ConfigureAppConfiguration, 比如我们自己的配置文件
4. StartUp, configureServices, 注入我们需要的组件以及服务
5. Startup, configure, 注入我们使用的中间件
StartUp
ConfigureServices
IdentityServer4在K8S的配置
services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); app.UseForwardedHeaders(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto });
分布式session
HttpClient
ThreadPool.SetMinThreads(20, 20);
VersionApi以及集成Swagger
json序列化配置
跨域处理
``` services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials() .SetPreflightMaxAge(TimeSpan.FromHours(1))); }); ``` 这里是允许所有的访问, 实际上, 可能只希望跨域给我们主域名的不同的二级域名或者三级域名访问. 需要参考 [一劳永逸:域名支持通配符,ASP.NET Core中配置CORS](https://www.cnblogs.com/dudu/p/5895424.html)
UseAuthentication和UseAuthorization
UserAuthentication是验证, 打个比分看看这个人是否能进入这个公司. 没有这个就不能解析识别cookie(或者jwt) UseAuthorization是用来读取claims的. 判断这个人在验证身份之后, 是否有进入这个公司的某些房间的权限.没有这个的话, 使用Authorize标志就会报错. 在某个Action上打上Authorize标记之后, 才可以在HttpContext.User.Identity里面读取到Claims信息. 比如IdentityServer4的cookie, 在startup里面会注册密钥. 在Authorize里面会解密cookie, 得到Claims
自定义中间件
统一错误处理
生成统一的RequestId并透传
支持body重复读取
k8scheck(k8s使用)
DI
在StartUp的configureService里面, 慎用BuildServiceProvider
public void ConfigureServices(IServiceCollection services) { services.AddSingleton<TestLife>(); var sp = services.BuildServiceProvider(); var t1 = sp.GetRequiredService<TestLife>(); t1.Name = "123"; var t2 = sp.GetRequiredService<TestLife>(); 这里产生的实例, 是容器的一个副本, 独立于整个系统之外 } 如果需要单例的话, 建议在 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { var sp = app.ApplicationServices; var t1 = sp.GetRequiredService<TestLife>(); t1.Name = "Configure"; var t2 = sp.GetRequiredService<TestLife>(); 这个里面初始化, 这样在后续的中间件里面, 得到的实例是在同一个生命周期里
tryAddSingleton和 AddSingleton
tryAddSingleton<IFoo, foo1> tryAddSingleton<IFoo, foo2> foo2并不会注册进去 AddSingleton<IFoo, foo1> AddSingleton<IFoo, foo2> 这两个都会注册进去. 如果用 IEnumable<IFoo> services来接受, 那么services.Count的长度==2 如果用 IFoo foo 来接受, 那么foo isInstanceOf foo2
tryAddEnumerable
tryaddsingleton<Ifoo, foo> 是如果Ifoo被重复注册多个, 那么后续的注册不会成功. TryAddEnumerable<ServiceDescriptor.Singleton<IFoo, foo1> TryAddEnumerable<ServiceDescriptor.Singleton<IFoo, foo2> 这个主要用来为同一个接口注册不同的实现. IEnumable<IFoo> services可以得到一个数组
移除和替换
services.RemoveAll<IFoo>
替换掉第一个实现
services.Replace(ServiceDescriptor.Singleton<IFoo, foo>)
泛型注册
service.AddSingleton(typoof(IRepository<>), typeof(Repository<>))
一个类实现了多个接口, 和一个接口多个继承
建议使用autofac
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); public void ConfigureContainer(ContainerBuilder builder) { // .net core3+ autofac, 更加简单了. 只需要在program里面增加一个autfac接管. // 这里新增现在这个方法即可. builder.RegisterModule(new ApplicationModule()); }
单例
使用单例作为自定义组件的配置项
public static void SetReverseProxy(this IServiceCollection services, Action<ProxyConfig> action) { services.AddHttpClient(); var proxyConfig = new ProxyConfig(); action.Invoke(proxyConfig); services.AddSingleton<ProxyConfig>(x=> proxyConfig); }
作用域和IDisposable
避免在容器里获取实现了IDisposable的对象, 也避免实现IDisposable
linq
where和或
linq 动态组装or查询条件 List<Product> GetProductsByOR(params string[] keywords) { DBDataContext db = new DBDataContext(Database.ConnectionString); var predicate = PredicateBuilder.False<Product>(); foreach (string keyword in keywords) { string temp = keyword; predicate = predicate.Or(p => p.Description.Contains(temp)); } var query = db.Products.Where(predicate); //翻译后的sql语句: //Select [t0].[ID], [t0].[Name], [t0].[Description] //FROM [dbo].[Product] AS [t0] //Where ([t0].[Description] LIKE '%6111%') OR ([t0].[Description] LIKE '%2350%') return query.ToList(); } 传统方式的, 只适合用于 and 的组合条件 var query = _repository.Table.Where(x => x.SiteId == siteId && x.Deleted == deleted); if (productId != 0) query = query.Where(x => x.Id == productId); if (!string.IsNullOrEmpty(title) && languageId == CommonHelper.ZhLanguageId) { query = query.Where(x => x.Name.Contains(title)); } else if (!string.IsNullOrEmpty(title) && languageId != CommonHelper.ZhLanguageId) { }
反向代理
nginx
代理别人的网站
对方要求 用反向代理去集成他们的站点, 每次都需要在header里面添加访问token. 如图方案.png 方案. 1. 开发一个反向代理程序, 用二级域名表示. 比如 qiyehuaxiang.onein.cn 就是反向代理了idspark-iframe.d.tencent.com 2. header参数, 第一次是通过获取iframe嵌入的url的queryString来传递的. 反向代理服务器在拿到queryString的时候, 会在response里加入cookie. 同时计算出header里面的值, 传递到对方. 3. 后面的请求, 浏览器会自行把cookie带上, 在反向代理程序里, 就能获取到cookie, 来计算header里面的值 
redis
sortedSet有序集合
排名
算法
一致性哈希
抽奖算法
递归
两个地理位置距离计算
newtonSoft