Дата изменения: 05.02.2020
HTTP — протокол, который не реализует отслеживание состояний. Это значит, что каждый новый запрос, будь он от того же пользователя из того же браузера – это новое независимое сообщение для сервера. Значения имеющие отношения к пользователю и состояние приложения никак не сохраняются между этими запросами.
Поэтому в Core подобная функциональность реализована программно в виде сессий. Сессии реализуют сохранение данных пользователя и приложения между запросами. Они имеют отношения к одному пользователю, существуют ограниченный период времени и разрываются при неактивности пользователя.
Сессии в Core
По умолчанию сессии MVC расположены в Куки, но могут располагаться в строке запроса.
Чтобы добавить поддержку сессий в проект сделайте следующее:
- подключите пакет Microsoft.AspNetCore.Session;
- добавьте хранилище сессий;
- затем зарегистрируйте службу и добавьте в конвейер:
После этого вы реализуете объект, который реализует сессию, создаете его и пользуетесь. Использование сессии сводится к трем базовым операциям с данными, которые она хранит: чтение, запись и проверка содержания.
и проверка того, содержится ли данное значение в сессии:
Все данные сессии хранят данные в виде массива байтов, которые легко привести к строкам или числовым значениям. Это важно, поскольку веб-приложения больше ориентированы на них.
Поэтому используйте методы расширения ISession:
- Get(ISession, String);
- GetInt32(ISession, String);
- GetString(ISession, String);
- SetInt32(ISession, String, Int32);
- SetString(ISession, String, String).
И обратное дествие:
Немного конфигурации
Настроить сессии можно при регистрации сервиса, через внутренний массив опций:
или при добавлении в конвейер:
Реализация сессий в приложении
Наполнение корзины покупок – типичный пример сессии: пользователь переходит со страницы на страницу, добавляет нужные ему книги в корзину, наполняя список покупок, который мы всегда сможем отобразить.
Модернизация сервиса
Наполнение корзины – сессия пользователя. Мы уже реализовали сервис доступа к данным через контекст добавим в него метод наполнения корзины:
ShoppingBag – класс, предназначенный для хранения ключа сессии и значимых данных при покупке. Мы реализуем его сразу после сервиса.
Добавим в сервис BookShopManager метод CreateOrder и реализуем его:
Метод получает список заказов и обновляет данные в БД пользуясь этим списком.
Метод Quantity экземпляра ShoppingBag по замыслу должно возвращать количество экземпляров одной книги, которые заказал пользователь. Запомним это.
ShoppingBag
Корзина представляет собой объект, который хранит данные в виде словаря идентификаторов заказанных книг, и имеет идентификатор сессии (просто строка).
Мы сделаем объект сериализуемым, и в связи с этим переопределим и расширим сессии.
Расширение ISession затрагивает методы Get и Set, также реализует комбинированный метод GetOrAdd, когда мы пишем в сессию мы сериализуем, когда запрашиваем из нее данные, предварительно десериализуем:
Теперь мы можем реализовать обработчики события (у нас пока нет пользовательского интерфейса) на определенные действия пользователя.
Очистка корзины покупок:
Визуализация представления с данными о покупках:
Помните, что по умолчанию сессии используют хранилище в памяти. Хранилище в памяти – самое быстрое, но в реальных приложениях почти не используется. Вы должны регистрировать поставщик Кэша до регистрации и настройки сессий, иначе подключится резервное хранилище в памяти.
Все сессии снижают производительность приложения, поэтому использовать их нужно только тогда, когда они удобны.
Объекты и данные объектов прекращают свое существования в сеансе, как только истечет время сеанса, по умолчанию – 30 минут.
Sessions in ASP.NET Core
ASP.NET Core is being designed so that your application is only dependent on features that it actually needs. This is achieved in large part by creating a composable framework, where the developer opts in to non-essential features — a number of which are baked in to traditional versions of ASP.NET. One of the features that this applies to is Session State. This article looks at how to obtain and use session state in ASP.NET Core applications.
If you are new to ASP.NET, session state is a mechanism that enables you to store and retrieve user specific values temporarily. These values can be stored for the duration of the visitor’s session on your site. In most cases, they are stored in server memory, although options exist for using persistent and/or distributed storage mechanisms if, for example, you are using multiple web servers for your application (web farm etc). This article is only interested in the in-memory option. The type of data you will use session state for is anything that relates to the current user. It might be their name, or a discount level they are entitled to, or anything that you don’t want to repeatedly query a database for.
Session management in ASP.NET Core is delivered via a pluggable component, or «middleware» and is available in a Nuget package called Microsoft.AspNet.Core.Session . When you use session management, you also need a persistence mechanism for session variables. In-memory storage is also available as an optional package called Microsoft.Extensions.Caching.Memory . To make them available to your application, add the following entries to the dependencies node of your project.json file:
You should see «Restoring. » appear next to the References node in Solution Explorer and the session package being added.
Now that the packages are available to your application, you can opt in to using them. You do this in the ConfigureServices method of the Startup class (Startup.cs file).
The highlighted lines show the code you need to add to the ConfigureServices method to register both caching and session with the services used in your application. The method takes an Action delegate that enables you to change the default options such as the location for session cookies, or the default timeout period (which is 20 minutes as in previous versions). The sample code above sets the timeout value to 30 minutes. This means that if a user is idle for more then 30 minutes, the session expires and its contents are removed from memory (if that is the backing store you choose to use). It also changes the name of the cookie used to manage sessions, which by default is .AspNet.Session .
At this point, you have included the required packages for session management within your solution and then registered them as services. Now you need to tell the application to use the session management features. You do this in the Configure method of the StartUp.cs file, which is where you register all middleware, but make sure you add session to the pipeline before adding MVC. :
You can start setting and getting session values after you have referenced Microsoft.AspNetCore.Http in your controller:
There are three methods that enable you to set session values: SetInt32 , SetString and Set , which takes a byte array as an argument. This is very different to the traditional session API, which allows you to set a session value by assigning any type to a session key. The new session framework stores items as byte arrays, and internally, SetInt and SetString converts the ints and strings that you pass to a byte array. The main reason behind this decision appears to be to ensure that session values are serialisable for storage on remote servers. The only way to store other types of values is to implement the serialisation to byte arrays yourself. I look at that shortly, but in the meantime, here’s how you would use the SetInt32 and SetString methods from within your controller to create and set some session variables:
And here’s how you might retrieve those values to be passed to a View:
There is also a Get method that returns a byte array. As I mentioned earlier, if you want to set other types as session variables, you need to take care of serialisation yourself. You could do this at the point of setting values, but a more reusable approach can be achieved by creating your own extension methods on ISession . The following example shows how you might implement GetBoolean and SetBoolean methods:
Now you can use these methods too:
There are two other methods of interest. One is the Remove method which allows you to delete individual values from the session collection by key:
The other is the Clear method. This removes all keys and values associated with the session. There is no obvious counterpart to the pre-ASP.NET Core Abandon method which ends the session.
Session Events
Classic ASP.NET includes a couple of session-related events: Session_Start and Session_End , which you can access via global.asax in order to execute code. In ASP.NET Core 1.0 , you can query the session collection using middleware to establish if a session has already been established to replicate the Session_Start event, but there are no plans to introduce an equivalent to Session_End . Since one of the driving forces behind ASP.NET Core is «cloud-readiness», the focus on session management design has been to make it work in a distributed scenario. Session_End only ever fired when sessions used inproc mode (local server memory) and the team have stated that they won’t add features that only work locally.
Webucator, a specialist provider of online ASP.NET training have produced a video version of this article:
Andrew Lock | .NET Escapades
An introduction to Session storage in ASP.NET Core
A common requirement of web applications is the need to store temporary state data. In this article I discuss the use of Session storage for storing data related to a particular user or browser session.
Options for storing application state
When building ASP.NET Core applications, there are a number of options available to you when you need to store data that is specific to a particular request or session.
One of the simplest methods is to use querystring parameters or post data to send state to subsequent requests. However doing so requires sending that data to the user’s browser, which may not be desirable, especially for sensitive data. For that reason, extra care must be taken when using this approach.
Cookies can also be used to store small bits of data, though again, these make a roundtrip to the user’s browser, so must be kept small, and if sensitive, must be secured.
For each request there exists a property Items on HttpContext . This is an IDictionary which can be used to store arbitrary objects against a string key. The data stored here lasts for just a single request, so can be useful for communicating between middleware components and storing state related to just a single request.
Files and database storage can obviously be used to store state data, whether related to a particular user or the application in general. However they are typically slower to store and retrieve data than other available options.
Session state relies on a cookie identifier to identify a particular browser session, and stores data related to the session on the server. This article focuses on how and when to use Session in your ASP.NET Core application.
Session in ASP.NET Core
ASP.NET Core supports the concept of a Session out of the box — the HttpContext object contains a Session property of type ISession . The get and set portion of the interface is shown below (see the full interface here):
As you can see, it provides a dictionary-like wrapper over the byte[] data, accessing state via string keys. Generally speaking, each user will have an individual session, so you can store data related to a single user in it. However you cannot technically consider the data secure as it may be possible to hijack another user’s session, so it is not advisable to store user secrets in it. As the documentation states:
You can’t necessarily assume that a session is restricted to a single user, so be careful what kind of information you store in Session.
Another point to consider is that the session in ASP.NET Core is non-locking, so if multiple requests modify the session, the last action will win. This is an important point to consider, but should provide a significant performance increase over the locking session management used in the previous ASP.NET 4.X framework.
Under the hood, Session is built on top of IDistributedCache , which can be used as a more generalised cache in your application. ASP.NET Core ships with a number of IDistributedCache implementations, the simplest of which is an in-memory implementation, MemoryCache , which can be found in the Microsoft.Extensions.Caching.Memory package.
MVC also exposes a TempData property on a Controller which is an additional wrapper around Session. This can be used for storing transient data that only needs to be available for a single request after the current one.
Configuring your application to use Session
In order to be able to use Session storage in your application, you must configure the required Session services, the Session middleware, and an IDistributedCache implementation. In this example I will be using the in-memory distributed cache as it is simple to setup and use, but the documentation states that this should only be used for development and testing sites. I suspect this reticence is due it not actually being distributed and the fact that app restarts will clear the session.
First, add the IDistributedCache implementation and Session state packages to your project.json:
Next, add the required services to Startup in ConfigureServices :
Finally, configure the session middleware in the Startup.Configure method. As with all middleware, order is important in this method, so you will need to enable the session before you try and access it, e.g. in your MVC middleware:
With all this in place, the Session object can be used to store our data.
Storing data in Session
As shown previously, objects must be stored in Session as a byte[] , which is obviously not overly convenient. To alleviate the need to work directly with byte arrays, a number of extensions exist for fetching and setting int and string . Storing more complex objects requires serialising the data.
As an example, consider the simple usage of session below.
This action simply simply returns a view with a model that shows the current time, and the time the session was initialised.
First, the Session is queried using GetString(key) . If this is the first time that action has been called, the method will return null. In that case, we record the current date, serialise it to a string using Newtonsoft.Json, and store it in the session using SetString(key, value) .
On subsequent requests, the call to GetString(key) will return our serialised DateTime which we can set on our view model for display. After the first request to our action, the DateSessionStarted property will differ from the Now property on our model:
This was a very trivial example, but you can store any data that is serialisable to a byte[] in the Session. The JSON serialisation used here is an easy option as it is likely already used in your project. Obviously, serialising and deserialising large objects on every request could be a performance concern, so be sure to think about the implications of using Session storage in your application.
Customising Session configuration
When configuring your session in Startup , you can provide an instance of StartupOptions or a configuration lambda to either the UseSession or AddSession calls respectively. This allows you to customise details about the session cookie that is used to track the session in the browser. For example you can customise the cookie name, domain, path and how long the session may be idle before the session expires. You will likely not need to change the defaults, but it may be necessary in some cases:
Note the cookie name is not the default .AspNetCore.Session :
It’s also worth noting that in ASP.NET Core 1.0, you cannot currently mark the cookie as Secure. This has been fixed here so should be in the 1.1.0 release (probably Q4 206/ Q1 2017).
Summary
In this post we saw an introduction to using Session storage in an ASP.NET Core application. We saw how to configure the required services and middleware, and to use it to store and retrieve simple strings to share state across requests.
As mentioned previously, it’s important to not store sensitive user details in Session due to potential security issues, but otherwise it is a useful location for storage of serialisable data.
Further Reading
- https://docs.asp.net/en/latest/fundamentals/app-state.html
- https://docs.asp.net/en/latest/performance/caching/distributed.html
- https://github.com/aspnet/Caching
- https://github.com/aspnet/Session
Enjoy this blog?
Asp net core session
No service for type ‘Microsoft.AspNetCore.Http.HttpContext’ has been registered.
im using this as guide, but seems to be not enough.
in home controller:
to this point it seems to be working, im able to navigate through different pages and i can use/check sessions in controller side.
But the problem is when im trying to use sessions in any view
can you please help?
Re: No service for type ‘Microsoft.AspNetCore.Http.HttpContext’ has been registered. sessions error | David Alejandro Garcia Garcia | 10-May-18 11:26 |
Session Timeout | Member 9812502 | 22-Nov-17 1:29 |
Does not work across instances | Nikodem Jaworski | 8-Nov-17 11:27 |
Last Visit: 17-Jun-20 8:27 Last Update: 17-Jun-20 8:27 | Refresh | 1 |
General News Suggestion Question Bug Answer Joke Praise Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
Управление состоянием приложения¶
В ASP.NET Core статусом приложения можно управлять разными способами. В этой статье мы рассмотрим некоторые варианты, а также установим и настроим поддержку состояния сессий в приложениях ASP.NET Core.
Опции Application State¶
Application state (состояние приложения) касается всех данных, которые используются для представления текущего состояния приложения. Это касается и глобальных, и конкретных данных. В предыдущих версиях ASP.NET (и даже ASP) имелась встроенная поддержка глобальных хранилищ состояний Application и Session , а также некоторые другие опции.
У хранилища Application были почти такие же характеристики, что и у ASP.NET Cache . В ASP.NET Core Application больше не существует; приложения, написанные для предыдущих версий ASP.NET и перемещенные в ASP.NET Core заменяют Application реализацией Caching .
Разработчики приложений вольны использовать различные провайдеры состояний приложений в зависимости от разных факторов:
- Как долго должны существовать данные?
- Какова величина данных?
- Каков формат этих данных?
- Можно ли их сериализовать?
- Насколько чувствительны данные? Могут ли они храниться на стороне клиента?
В зависимости от ответов на эти вопросы состояние приложения ASP.NET Core может управляться разными способами.
HttpContext.Items¶
Коллекция Items — это лучшее место для хранения данных, которые нужны для обработки определенного запроса. Их содержание сбрасывается после каждого запроса. Это что-то вроде связки между компонентами и middleware, которые работают на разных этапах запроса и не имеют прямого отношения к данным, передающим параметры и возвращаемые значения. См. Работа с HttpContext.Items.
Строка запроса и Post¶
Состояние запроса может быть передано другому запросу, если вы добавите значение строке нового запроса или используете POST. Такие технологии не нужно использовать с чувствительными данными, поскольку здесь данные отправляются клиенту, а затем обратно серверу. Лучше всего использовать такой способ с небольшим количеством данных. Строки запросов особенно полезны для определения состояния постоянным образом, и таким образом создаются ссылки, сохраняющие состояние, а затем они отправляются через имейлы или социальные сети для возможного дальнейшего использования. Поскольку URL со строками запросов можно легко делиться, вы должны позаботиться об избежании CSRF-атак) (например, даже если предположить, что только зарегистрированные пользователи могут выполнять некоторые действия с помощью таких URL, атакующий может попробовать использовать такой URL как зарегистрированный пользователь).
Некоторое количество данных, связанных с состоянием, может быть сохранено в куки. Они отправляются с каждым запросом, так что их размер должен быть минимальным. В идеале, нужно использовать только идентификатор с актуальными данными, сохраненными на сервере, которые связаны с идентификатором.
Сессия¶
Сессионное хранилище использует куки-идентификатор, чтобы получить доступ к данным, связанным с определенной сессией браузера (серией запросов от определенного браузера). Вы не должны думать, что сессия ограничена одним пользователем, так что аккуратно храните информацию в разделе Session. Это хорошее место для хранения состояния приложения, которое касается определенной сессии, но которое не нужно отслеживать постоянно (или которое можно восстановить из хранилища состояний). См. Установка и настройка сессий.
Кэширование помогает сохранить и эффективно извлекать произвольные данные приложения, основываясь на ключах, определенных разработчиком. Здесь есть правила по истекающим кэшированным элементам, и это зависит от временных и других условий. См. Caching .
Конфигурация¶
Конфигурация может считаться некоторой формой хранилища данных приложения, хотя обычно во время запуска приложения она является read-only. См. Конфигурация .
Другие постоянные хранилища¶
Другие постоянные хранилища, например, Entity Framework и база данных или Azure Table Storage также могут служить для сохранения состояния приложения, но ASP.NET напрямую их не поддерживает.
Работа с HttpContext.Items¶
HttpContext поддерживает коллекцию типа IDictionary object> , которая называется Items . Эта коллекция доступна при запуске HttpRequest` и сбрасывается в конце каждого запроса. Вы можете получить к ней доступ при помощи запроса или присвоения значения ключу.
Например, Связующее ПО (Middleware) может что-то добавить в коллекцию Items :
и далее в потоке другой кусок связующего ПО может добраться до этого:
Ключи в Items являются простыми строками, и если вы разрабатываете связующее ПО, которое должно работать с несколькими приложениями, вам стоит добавить ключам уникальный идентификатор, чтобы избежать проблем с ключами (например, “MyComponent.isVerified” вместо “isVerified”).
Установка и настройка сессий¶
В ASP.NET Core есть пакет, который предоставляет связующее ПО для управления состоянием приложения. Вы можете установить его, добавив ссылку на пакет в файл project.json.
После установки пакета нужно настроить Session в классе Startup . Session создается вверху IDistributedCache , так что вам нужно настроить все это, иначе вы получите ошибку.
Если вы не настроите хотя бы одну реализацию IDistributedCache , у вас появится исключение “Unable to resolve service for type ‘Microsoft.Extensions.Caching.Distributed.IDistributedCache’ из-за попытки активировать ‘Microsoft.AspNet.Session.DistributedSessionStore’.”
ASP.NET поставляется с несколькими реализациями IDistributedCache , включая опцию in-memory (используется только во время разработки и тестирования). Чтобы настроить сессию при помощи этой опции, добавьте Microsoft.Extensions.Caching.Memory в пакет project.json, а следующее в ConfigureServices :
Затем добавьте следующее в Configure перед app.UseMVC() , и вы будете готовы использовать сессию:
После установки и настройки вы будете готовы использовать Session из HttpContext .
Если вы попытаетесь использовать Session перед вызовом UseSession , у вас появится исключение InvalidOperationException , что будет написано следующее: “Session has not been configured for this application or request.”
Если вы попытаетесь создать новую Session (пока не было создано куки сессии) после того, как вы начали создавать поток Response , у вас также появится исключение InvalidOperationException , где будет говорится, что “The session cannot be established after the response has started”. Это исключение, возможно, не будет отображено в браузере; вам потребуется просмотреть серверный лог, чтобы найти его:
Детали реализации¶
Сессия использует куки, чтобы отслеживать и устранять неоднозначные моменты между запросами с разных браузеров. По умолчанию этот куки называется ”.AspNet.Session”. Далее, по умолчанию этот куки не указывает домен, и он недоступен для скриптов на клиентской стороне (поскольку CookieHttpOnly установлен на true ).
IdleTimeout (используется на сервере независимо от куки) можно переписать при настройке Session при помощи SessionOptions :
IdleTimeout используется сервером для определения того, когда сбрасывается сессия. Каждый запрос передается связующему ПО Session (независимо от того, считывается или записывается Session внутри данного ПО), и это сбрасывает таймаут. Обратите внимание, что все это происходит независимо от сброса куки.
Если два запроса пытаются изменить контекст сессии, сделает это последний. Кроме того, все виды контекста сессии сохраняются совместно. Это обозначает, что если два запроса меняют различные части сессии (разные ключи), они будут все равно влиять друг на друга.
ISession¶
После установки и настройки сессии вы ссылаетесь на нее через HttpContext, который предоставляет свойство Session типа ISession. Вы можете использовать этот интерфейс, чтобы получать и устанавливать значения в Session , в качестве byte[] .
Поскольку Session создается вверху IDistributedCache , вы всегда должны сериализовать сохраняемые экземпляры объекта. Интерфейс работает с byte[] , а не просто с object . Однако существуют методы расширения, которые упрощают работу с простыми типами, такими как String и Int32 , а также упрощают получение значений byte[] из сессии.
Если вы сохраняете более сложные объекты, вам нужно сериализовать объект в byte[] , чтобы его сохранить, а при получении десериализовать его.
Рабочий пример использования сессии¶
В данном примере показано, как работать с Session, включая сохранение и получение простых типов, а также пользовательских объектов. Если вы хотите узнать, что происходит после истечения сессии, то вы увидите это на данном примере, поскольку сессия длится всего 10 секунд:
Когда вы впервые переходите на веб сервер, тут представлен скриншот, показывающий, что сессия еще не была установлена:
Такое поведение по умолчанию происходит из-за связующего ПО в Startup.cs , который запускается тогда, когда делается запрос, у которого нет установленной сессии (обратите внимание на выделенные сессии):
GetOrCreateEntries — это вспомогательный метод, который получает экземпляр RequestEntryCollection из Session , если сессия существует; в ином случае он создает коллекцию и возвращает ее. Коллекция содержит экземпляры RequestEntry , в которых находятся различные запросы, которые сделал пользователь во время текущей сессии.
Типы, которые хранятся в сессии, помечаются [Serializable] .
Извлечение текущего экземпляра RequestEntryCollection происходит при помощи вспомогательного метода GetOrCreateEntries :
Если в Session существует объект, он извлекается как тип byte[] , а затем десериализуется с помощью MemoryStream и BinaryFormatter . Если объект не находится в Session , метод извлекает новый экземпляр RequestEntryCollection .
В браузере нажатие гиперссылки “Establish session” направляет запрос по пути “/session” и возвращает такой результат:
Обновление страницы включает счетчик; возврат к корню сайта (после нескольких запросов) имеет вот такой итог, то есть, здесь подсчитаны все запросы, которые были сделаны во время этой сессии:
Работа с сессиями происходит с помощью связующего ПО, которое направляет запросы на “/session”:
Запросы по этому пути получат или создадут RequestEntryCollection , добавят к ней текущий путь, а затем сохранят сессию с помощью вспомогательного метода SaveEntries :
SaveEntries показывает, как превратить пользовательский объект в byte[] для сохранения в Session с помощью MemoryStream и BinaryFormatter .
Пример включает в себя некоторое связующее ПО, которое стоит упомянуть, и оно работает с путем “/untracked”. Вот его настройки: