DotNetCore Web应用程序中的Session管理

原文来自互联网,由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除。限于译者的能力有限,个别语句翻译略显生硬,还请见谅。

作者简介:Jon(Jonathan)Seeley,一位资深.NET开发者,主要从事Asp.NET/Asp.NET CORE/WPF等技术栈的开发,他的博客地址为https://www.seeleycoder.com/。

查看原文[1]

上个月,我们讨论了DotNetCore Web应用程序中的Cookie管理,[2]并介绍了通用Cookie服务。今天,我们将研究一种用于存储用户数据的替代选项。会话状态是链接到浏览会话的信息的服务器存储,让我们看一下dotnetcore Web应用程序中的通用会话管理技术。

这篇文章的所有代码都可以在我的GitHub上找到[3]

开始之前

如果您在该领域工作了一段时间,您可能已经注意到很多教程向您展示了如何做某事。我的意思是,那就是他们在那里的权利吗?他们通常不会向您显示的是*

正确地

的操作方法*。我今天要做同样的事情。可是等等!请允许我快速解释。今天,我们将在此会话中使用内存中会话状态。

从本质上说,这没错,但也不对。对于任何大小的应用程序,您都希望使用非易失性会话提供程序。例如Redis,数据库,XML文件或其他内容。好吧,Redis实际上也在内存中,对吗?是的,我们不要被语义所困扰。他们将其作为单独的服务运行。

入门

假设您刚刚加载了控制台并输入了以下命令:dotnet new mvc -n SessionManager

现在,您拥有一个崭新的网站品牌,在撰写本文时,该网站正在使用netcoreapp2.2。精彩。

让我们打开它并转到Startup.cs文件,然后在ConfigureServices调用中添加以下几行。这将注册ISession到请求生命周期中,并允许我们从httpcontext访问它。我们还将公开IHttpContextAccessor注入,因为稍后需要使用它。

// have to add this in order to access HttpContext from our own services
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// register ISession into our HttpContext lifecycle
services.AddSession(options =>
{options.Cookie.HttpOnly = true;
});

ISession无法正常注入,但是您可以通过提供者工厂做一些魔术来将其公开为可注入对象。为了我们的目的,我们不需要,但让我们假装您想要。您可以执行以下操作:

 services.AddScoped(provider => provider.GetRequiredService().HttpContext?.Session);

我们还没有完成。我们需要指示管道实际使用会话。弹出该Configure方法,并app.UseSession();在app.UseMvc(...)调用之前添加某处。

添加SessionService

由于我们不会直接注入ISession,因此会出现“我们该怎么办?”的问题。让我们创建一个ISessionService并使用一些通用方法进行设置,以便我们可以序列化/反序列化我们想要的任何内容。如果您查看过Cookie管理方面[4]的信息,则看起来非常相似。

public interface ISessionService
{T Get<T>(string key);void Set<T>(string key, T value);void Remove(params string[] keys);
}
public class SessionService : ISessionService
{private readonly ISession _session;public SessionService(IHttpContextAccessor httpContextRepository){_session = httpContextRepository.HttpContext.Session;if (_session == null)throw new ArgumentNullException("Session cannot be null.");}public T Get<T>(string key){throw new NotImplementedException();}public void Set<T>(string key, T value){throw new NotImplementedException();}public void Remove(params string[] keys){throw new NotImplementedException();}
}

在上面的代码块中,我们为ISessionService创建了存根,并为实现创建了框架。我们注入了IHttpContextAccessor,然后从HttpContext访问了Session属性。我们进行检查以确保Session不为空。如果您尝试将其注入通常不具有HttpContext的某个地方(可能是托管服务等),则可能会发生这种情况。我们需要跳回到Startup.cs并添加services.AddScoped<ISessionService, SessionService>();到我们的ConfigureServices调用中。只需在AddSession通话后直接添加即可,但诚实订购无所谓。

实施SessionService

让该服务变得有趣。进入并将以下代码添加到方法中:

public T Get<T>(string key)
{string value = _session.GetString(key);if (string.IsNullOrWhiteSpace(value))return default(T);return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(value);
}
public void Set<T>(string key, T value)
{if (value == null)Remove(key);else_session.SetString(key, Newtonsoft.Json.JsonConvert.SerializeObject(value));
}
public void Remove(params string[] keys)
{if (keys.IsNullOrEmpty())return;foreach (string key in keys)_session.Remove(key);
}

很基本。在该Get方法中,我们从会话中获取字符串值,如果该字符串值不存在,则返回我们类型的默认值。否则,我们将从JSON反序列化为该类型。Set如果传递null或将JSON序列化到会话,我们的方法将删除该值。

我们的Remove方法只是要求会话删除一个值。您不必先检查它是否存在,那样的会话很友好。它确实引用了我添加的扩展方法IsNullOrEmpty,该方法只是检查IEnumerable(在本例中为字符串数组)中是否包含任何内容。

整合代码

打开HomeController.cs并对其进行修改,如下所示:

public class HomeController : Controller
{private const string c_CONTRIVEDSESSIONKEY = "contrived";private const string c_NAMESESSIONKEY = "basicname";private readonly ISessionService _sessionService;public HomeController(ISessionService sessionService){_sessionService = sessionService;}public IActionResult Index(){var name = _sessionService.Get<string>(c_NAMESESSIONKEY);var contrived = _sessionService.Get<ContrivedValues>("contrived") ?? new ContrivedValues { Name = "Guest" };var viewModel = new HomeViewModel{Name = name,Contrived = contrived};return View(viewModel);}[HttpPost]public IActionResult PostBasic(NameRequest request){_sessionService.Set(c_NAMESESSIONKEY, request.Name);return RedirectToAction(nameof(Index));}[HttpPost]public IActionResult PostContrived(ContrivedValues request){_sessionService.Set(c_CONTRIVEDSESSIONKEY, request);return RedirectToAction(nameof(Index));}public IActionResult DeleteContrived(){_sessionService.Remove(c_CONTRIVEDSESSIONKEY);return RedirectToAction(nameof(Index));}public IActionResult Privacy(){return View();}[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]public IActionResult Error(){return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });}
}

在上面的代码中我们注入我们的新ISessionService进HomeController,然后建立一些非常蹩脚的,以及如何与它进行交互的例子。HomeController:Index例如,该方法将尝试从会话中获取字符串值以及一个实例(ContrivedValues如果找不到)或默认实例。我们将它们包装到viewModel中,然后传递到视图中。

HomeController:PostBasic操作方法接受一个NameRequest岗位价值,并将其设置到会话中。PostContrived接一个ContrivedValues帖子并设置它。DeleteContrived将删除我们的会话对象ContrivedValues。

视图模型

显然,以上内容尚未编译。我们需要添加一些模型。进入“模型”文件夹,然后将以下代码添加到新文件中HomeViewModel.cs。

public class HomeViewModel
{public string Name { get; set; }public ContrivedValues Contrived { get; set; }
}
public class NameRequest
{public string Name { get; set; }
}
public class ContrivedValues
{public int? Age { get; set; }public string Name { get; set; }
}

Index.cshtml(视图/主页) 最后,我们需要更新视图以处理该演示。

@model SessionManagerDemo.Models.HomeViewModel
@{ViewData["Title"] = "Home Page";
}
<div class="row"><div class="col-md-3"><h2>Basic Cookie</h2>@if (!string.IsNullOrWhiteSpace(Model.Name)){<p>Your name is: @Model.Name</p>}<form action="@Url.Action("PostBasic")" method="post"><div class="form-group"><label for="name">Name: </label><input type="text" class="form-control" name="name" placeholder="Name" /></div><button type="submit" class="btn btn-primary">Submit</button></form></div><div class="col-md-3"><h2>Cookie w/Default</h2><p>Current values:</p><ul><li>Age: @Model.Contrived.Age</li><li>Name: @Model.Contrived.Name</li></ul><form action="@Url.Action("PostContrived")" method="post"><div class="form-group"><label for="name">Name: </label><input type="text" class="form-control" name="name" placeholder="Name" /></div><div class="form-group"><label for="name">Age: </label><input type="number" class="form-control" name="age" placeholder="Age" /></div><button type="submit" class="btn btn-primary">Submit</button><!-- yeah yeah, I know... this is naughty --><a href="@Url.Action("DeleteContrived")" class="btn btn-danger">Delete</a></form></div>
</div>

单元测试

我不会在此处粘贴单元测试,但是GitHub代码中[5]有一些单元测试也可以体现其功能。ISession在这种情况下,替换的确很有趣,因为我们使用的是ISession.GetString,它实际上是包装ISession.TryGetValue调用的扩展方法。随意看看,看看我是如何做到这一点的。

结论

DotNetCore Web应用程序中的会话管理非常简单。我们只需要注册会话中间件和会话服务,但是它本身是非常有限的。添加基于通用的包装器可以使我们对过程有更多的控制和灵活性。对于除基本以外的任何功能,您都希望使用某种会话状态服务器,而不是使用此示例所示的内存中状态。

今天发布的所有代码都可以在我的GitHub上找到[6]

References

[1] 查看原文: https://www.seeleycoder.com/blog/session-management-dotnetcore/#more-413
[2] DotNetCore Web应用程序中的Cookie管理,: https://www.seeleycoder.com/blog/cookie-management-asp-net-core/
[3] 我的GitHub上找到: https://github.com/fooberichu150/SessionService
[4] Cookie管理方面: https://www.seeleycoder.com/blog/cookie-management-asp-net-core/
[5] GitHub代码中: https://github.com/fooberichu150/SessionService
[6] 我的GitHub上找到: https://github.com/fooberichu150/SessionService

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css3是什么 ptml_CSS3

CSS3HTMLCSSJavaScript结构表项交互如何学习&#xff1f;CSS是什么CSS怎么用(快速入门)CSS选择器(重点难点)美化网页(文字、阴影、超链接、列表、渐变...)盒子模型浮动定位网页动画(特效效果)1、初识CSS1.1、什么是CSSCascading Style Sheet(层叠样式表)CSS&#xff1a;表现(美…

python docx runs_别再问我Python怎么操作Word了!

安装docx是一个非标准库&#xff0c;需要在命令行(终端)中使用pip即可安装pip install python-docx一定要注意&#xff0c;安装的时候是python-docx而实际调用时均为docx!前置知识Word中一般可以结构化成三个部分&#xff1a;文档Document段落Paragraph文字块Run也就是Document…

【要闻】如何基于K8s管理1600个微服务?某数字化银行秘诀公开

Cloud Foundry Foundation宣布KubeCF为新孵化项目Cloud Foundry Foundation是开放源代码项目的聚集地&#xff0c;简化了开发人员的体验&#xff0c;近日其宣布&#xff0c;KubeCF已成为该基金会的孵化项目&#xff0c;并已发布版本1.0.1。KubeCF是Cloud Foundry应用程序运行时…

如何用 Blazor 实现 Ant Design 组件库?

本文主要分享我创建 Ant Design of Blazor 项目的心路历程&#xff0c;已经文末有一个 Blazor 线上分享预告。Blazor WebAssembly 来了&#xff01;Blazor 这个新推出的前端 Web 框架&#xff0c;想必是去年 .NET Core 3.0 发布时才进入 .NET 开发者的视线的。但其实&#xff0…

.NET Core开发实战(第28课:工作单元模式(UnitOfWork):管理好你的事务)--学习笔记...

28 | 工作单元模式&#xff08;UnitOfWork&#xff09;&#xff1a;管理好你的事务工作单元模式有如下几个特性&#xff1a;1、使用同一上下文2、跟踪实体的状态3、保障事务一致性我们对实体的操作&#xff0c;最终的状态都是应该如实保存到我们的存储中&#xff0c;进行持久化…

《C++ Primer》7.3.2节练习

练习7.27: #include <iostream> #include <cstring> using namespace std;class Screen {private:unsigned height 0, width 0;unsigned cursor 0;string contents;public:Screen() default;Screen(unsigned ht, unsigned wd): height(ht), width(wd), conten…

【实战 Ids4】║ 控制台密码模式搭配Ocelot网关

&#xff08;此岁只能云赏樱了&#xff09;书接上文&#xff0c;这些天一直在研究IdentityServer4&#xff08;下文简称Ids4&#xff09;框架&#xff0c;发现有很多有意思&#xff0c;或者说比我想象中的知识点&#xff0c;可扩展的多&#xff0c;所以比较开心能钻研进去&…

《C++ Primer》7.3.3节练习

练习7.31: 满足题意的程序如下所示&#xff1a; class X;//声明类型X class Y//定义类型Y {X* x; }; class X//定义类型X {Y y; };类X的声明称为前向声明&#xff0c;它向程序中引入了名字X并且指明X是一种类类型。对于类型X来说&#xff0c;此时我们已知它是一个类类型&#…

.NET Core 如何生成信用卡卡号

点击上方蓝字关注“汪宇杰博客”导语上个月我写了《.NET Core 如何验证信用卡卡号》&#xff0c;不少朋友表示挺有兴趣。在金融科技行业的实际工作中&#xff0c;通常还需要生成信用卡卡号用来测试&#xff0c;今天我就来教大家如何生成信用卡卡号。上回的改进上篇文章写完后&a…

python怎么爬虎牙_使用python爬虫框架scrapy抓取虎牙主播数据

前言本文利用python的scrapy框架对虎牙web端的主播、主播订阅数、主播当前观看人数等基本数据进行抓取&#xff0c;并将抓取到的数据以csv格数输出&#xff0c;以及存储到mongodb中思路观察虎牙网站后确认所有频道url都在www.huya.com/g中的&#xff0c;而主播房间数据则是ajax…

《C++ Primer》7.3.4节练习

练习7.32: 要想让clear函数作为Screen的友元&#xff0c;只需要在Screen类中做出友元声明即可。本题的真正关键之处是程序的组织结构&#xff0c;我们必须首先定义Window_mgr类&#xff0c;其中声明clear函数&#xff0c;但是不能定义它&#xff1b;接下来定义Screen类&#xf…

.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记

29 | 定义仓储&#xff1a;使用EF Core实现仓储层首先定义仓储层的接口&#xff0c;以及仓储层实现的基类&#xff0c;抽象类仓储层的接口namespace GeekTime.Infrastructure.Core {/// <summary>/// 包含普通实体的仓储/// 约束 TEntity 必须是继承 Entity 的基类&#…

ueditor单图上传iframe跨域_UEditor单图上传(simpleupload)跨域问题解决方案

代码实现首先我们需要在ueditor.all.js文件中找到原本的单图上传部分的代码搜索关键字 simpleupload&#xff0c;如下图所示&#xff1a;然后找到上传图片的代码片段&#xff0c;如下图所示&#xff1a;然后把 domUtils.on的 input 绑定的事件注释掉或删除掉替换成以下代码:inp…

StringBuilder内存碎片对性能的影响

TL;DR:StringBuilder内部是由多段 char[]组成的半自动链表&#xff0c;因此频繁从中间修改 StringBuilder&#xff0c;会将原本连续的内存分隔为多段&#xff0c;从而影响读取/遍历性能。连续内存与不连续内存的性能差&#xff0c;可能高达 1600倍。背景用 StringBuilder的用户…

java 双击_利用java开发一个双击执行的小程序

之前我们利用java写了很多东西&#xff0c;但是好像都没有什么实际意义。因为有意义桌面小程序怎么都得有个界面&#xff0c;可是界面又不太好搞。或者 了解到这一层的人就少之又少了。呀&#xff0c;是不是还得开辟一些版面来介绍awt和 swing。。。算了 先把这个 双击执行的小…

开发人员如何学习 Kubernetes

虽然“容器编排平台”还没有被整个行业大范围采用&#xff0c;但在这一领域 Kubernetes 已经战胜其他选手&#xff0c;成为了事实标准。近两年的 Web 开发技术社区&#xff0c;随便打开一两个群&#xff0c;你都能看到人们在谈 Kubernetes。很多开发人员&#xff0c;包括曾经的…

安装 java decompiler_Eclipse离线安装Java Decompiler插件(反编译)

Java Decompiler是Java语言的反编译工具&#xff0c;具体介绍见博客Java Decompiler(Java反编译工具)1、下载插件Eclipe的Java Decompiler插件名为JD-Eclipse&#xff0c;2、安装插件Ecipse安装JD-Eclipse(即Java Decompiler)插件步骤如下&#xff1a;打开Help --> Install …

给 ABP vNext 应用安装私信模块

在上一节五分钟完成 ABP vNext 通讯录 App 开发 中&#xff0c;我们用完成了通讯录 App 的基础开发。这本章节&#xff0c;我们会给通讯录 App 安装私信模块&#xff0c;使不同用户能够通过相互发送消息&#xff0c;并接收新私信的通知。在章节的最后&#xff0c;笔者将演示模块…

《C++ Primer》7.5.2节练习

练习7.41: #include <iostream> #include <string> using namespace std;class Sales_data {friend std::istream &read(std::istream &is, Sales_data &item);friend std::ostream &print(std::ostream &os, const Sales_data &item);pu…

零基础玩视频号?创作运营变现,你要的干货都在这了!

点击蓝字“大白技术控”关注我哟加个“星标★”&#xff0c;每日良时&#xff0c;好文必达&#xff01;不少小伙伴应该已经听说过视频号这个新功能了&#xff0c;视频号是微信内测的短视频功能&#xff0c;本人已经在视频号里刷了2个月了。3月中旬正式开通了视频号 「大白技术控…