​

ASP.NET Core基本原理(11)-管理应用程序状态[译]

在ASP.NET Core中,应用程序的状态可以有多种方式进行管理,取决于何时以及如何检索状态。

应用程序状态的可选方式

应用程序状态指用于描述应用程序当前状态的任何数据。这包含全局和特定于用户的数据。ASP.NET之前的版本已经有内置的对全局​​Application​​ 和 ​​Session​​ 状态的支持。

应用程序开发者可以根据不同的因素来自由选择不同的方式存储状态数据:

  • 数据需要持续多长时间?
  • 数据有多大?
  • 数据的格式是什么?
  • 数据是否可以序列化?
  • 数据有多敏感?能不能保存在客户端?

基于这些问题的答案,在ASP.NET Core以不同的方式存储和管理应用程序的状态。

HttpContext.Items

当数据只用于一个给定请求进行处理时,​​Items​​集合是存储数据的最佳位置。数据内容将在每个请求结束之后被丢弃。可以作为组件或者中间件在一个请求期间的不同时间点进行通信的最佳手段,并且彼此之间没有直接关系。

QueryString and Post

在新的请求查询字符串中添加值或者通过POSTing数据,可以将一个请求的状态数据提供给另一个请求。这种技术不应该用于敏感性的数据,因为这种技术需要将数据发送给客户端,然后再发送回服务器。也最好用于少量的数据。查询字符串对于持久地保留状态特别有用,允许创建具有嵌入状态的链接并通过Email和社交网络进行发送,没有对做出请求的用户做出假设,因为具有查询字符串的URL可以很容易被分享,所以必须小心避免跨站请求伪装攻击 (Cross-Site Request Forgery, CSRF)(例如,即使假设只有验证的用户才能够使用基于URL的查询字符串,攻击者还是诱骗已经验证过的用户去访问这样的URL)。

Cookies

与状态有关的少量数据可以存储在Cookies中。它们给随每次请求被发送,所以它的大小应该保存最小化。理想情况下,应该只使用一个标识符,而真正的数据存储在服务器中。

Session

Session存储依靠一个基于Cookie的标识符来访问与给定浏览器Session相关的数据(来自一个特定浏览器和机器的一系列请求)。你不能假设一个Session只限定给一个用户。所以要谨慎考虑在Session中存储哪类信息。这是一个很好的地方来存储具体到一个特定Session但不需要持久保存的应用程序状态。

Cache

缓存提供了一种基于开发人员定义的键来存储和有效检索任意应用程序数据的方法。它提供了基于时间和其他因素来使缓存过期的规则。

Configuration

配置可以认为是应用程序状态存储的另一种形式,但是通常它的应用程序运行时是只读的。

使用HttpContext.Items

​HttpContext​​抽象通过​​Items​​提供了对一个简单的​​IDictionary<object, object>​​类型的字典集合的支持。在每个请求中,这个集合从​HttpRequest​。你不能假设一个Session只限定给一个用户。所以要谨慎考虑在Session中存储哪类信息。这是一个很好的地方来存储具体到一个特定Session但不需要持久保存的应用程序状态。

例如,一个简单的中间件可以向​​Items​​集合中添加一些内容:



app.Use(async (context, next) =>
{
// perform some verification
context.Items["isVerified"] = true;
await next.Invoke();
});


随后,在管道中,其它的中间件可以访问到它:



app.Run(async (context) =>
{
await context.Response.WriteAsync("Verified request? " + context.Items["isVerified"]);
});


安装和配置Session

ASP.NET Core提供了一个会话包,它提供了用于管理会话状态的中间件。你可以在project.json文件中加入对​​Microsoft.AspNetCore.Session​​包的引用来安装它。

一旦安装好程序包,必须在​​Startup​​类中对Session进行配置。Session是基于​​IDistributedCache​​进行构建的,因此也必须配置它,否则你会得到一个错误。

注:如果你一个​​IDistributedCache​​实现都没有配置,则会得到一个异常 - “Unable to resolve service for type ‘Microsoft.Extensions.Caching.Distributed.IDistributedCache’ while attempting to activate ‘Microsoft.AspNetCore.Session.DistributedSessionStore’.”

ASP.NET提供了​​IDistributedCache​​的多种实现,包含in-memory选项(仅用于开发和测试期间)。要配置Session使用采用in-memory选项,将​​Microsoft.Extensions.Caching.Memory​​依赖项添加到你的project.json中,然后再添加如下代码到​​ConfigureServices​​:



services.AddDistributedMemoryCache();
services.AddSession();


然后,添加如下代码到​​Configure​​,并且添加到​​app.UseMVC()​之前,这样你就可以在程序代码中使用会话了:



app.UseSession();


安装和配置好之后,你就可以从​​HttpContext​​中引用Session了。

注:如果你在调用​​UseSession​​之前尝试访问​​Session​​,你将会得到一个​​InvalidOperationException​​异常 - “Session has not been configured for this application or request.”

实现细节

Session是利用一个cookie来跟踪和区分不同浏览器之间的请求的。默认情况下,这个cookie命名为”.AspNet.Session”,并使用一个路径”/”。另外,在默认情况下,这个cookie不指定域,而且对于页面上的客户端脚本是不可使用的(因为​​CookieHttpOnly​​的默认值是​​true​​)。

这些默认值,以及​​IdleTimeout​​(独立于cookie在服务端使用),都可以通过​​SessionOptions​​在配置​​Session​​的时候重写:



services.AddSession(options =>
{
options.CookieName = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
});


​IdleTimeout​​被服务端用于决定在Session的内容被抛弃之前Session可以闲置多久。每一个请求通过Session中间件(无论中间件对Session是读取还是写入)都将会重置超时时间。注意,这与cookie的过期时间是独立的。

ISession

一旦Session安装和配置完成,你就可以通过​​HttpContext​​所暴露的一个​​ISession​​类型名为​​Session​​的属性来引用它。



public interface ISession
{
bool IsAvailable { get; }
string Id { get; }
IEnumerable<string> Keys { get; }
Task LoadAsync();
Task CommitAsync();
bool TryGetValue(string key, out byte[] value);
void Set(string key, byte[] value);
void Remove(string key);
void Clear();
}


因为​​Session​​构建在​​IDistributedCache​​之上,你必须总是序列化被存储的对象实例。因此,这个接口使用​​byte[]​​而不是直接使用​​object​​。不过,有扩展方法可以让使用诸如​​String​​ 和 ​​Int32​​变得更容易。



// session extension usage examples
context.Session.SetInt32("key1", 123);
int? val = context.Session.GetInt32("key1");

context.Session.SetString("key2", "value");
string stringVal = context.Session.GetString("key2");

byte[] result = context.Session.Get("key3");


如果要存储更复杂的对象,你需要把对象序列化为一个​​byte[]​​以便存储它们,在获取对象的时候,要将它们从​​byte[]​​进行反序列化。