
Просмотрщик без состояния — Как создать минимальный открытый эндпоинт .NET 8
Просмотрщик без состояния — по дизайну
Viewer не хранит никаких данных документа, специфичных для запроса. Это запечатанный класс, он никогда не реализует IDisposable, и каждый документ, который вы открываете, живёт в отдельной сессии, управляемой кэшем фреймворка. Благодаря этому вы можете внедрить единственный экземпляр Viewer в любом месте вашего приложения ASP.NET Core и безопасно вызывать его из множества одновременных запросов.
Последствия безсостояния сервиса
- Нет скрытых ресурсов – сам сервис никогда не выделяет большие буферы, требующие явной очистки.
- Потокобезопасность по умолчанию – поскольку не хранится состояние, специфичное для запроса, тот же экземпляр
Viewerможет использоваться несколькими потоками без блокировок. - Масштабируемые развертывания – горизонтальное масштабирование работает сразу; вам не нужны привязанные сессии или привязка к серверу, только токен сессии, который держит клиент.
Вся информация, специфичная для документа (страницы, аннотации, поисковый индекс), хранится в сессии, созданной OpenDocumentAsync. Сессия сохраняется в IMemoryCache (или любом IDistributedCache, который вы настроите) и автоматически истекает после периода бездействия (по умолчанию 60 минут). Когда вы закончите, вы можете вызвать CloseDocument, чтобы освободить запись кэша ранее.
Регистрация безсостояния просмотрщика
Поскольку у Viewer нет изменяемого состояния, зарегистрируйте его один раз при запуске:
builder.Services.AddDoconut(); // регистрирует Viewer и необходимое промежуточное ПО
builder.Services.AddSession(); // требуется для безопасности токена
builder.Services.AddDistributedMemoryCache(); // простой кэш для демонстрации
Вызов AddDoconut также подключает middleware, обслуживающий статические ресурсы просмотрщика. После того как сервисы настроены, вы можете запросить Viewer в любой сигнатуре эндпоинта.
Минимальный эндпоинт открытого документа (пример net‑8)
Следующий код — точный фрагмент из официальной документации и работает сразу в проекте .NET 8 minimal‑API:
app.MapPost("/api/open", async (string fileName, Viewer viewer) =>
{
var token = await viewer.OpenDocumentAsync($"files/{fileName}");
return Results.Content(token, "text/plain");
});
Что делает код
- Принимает имя файла – клиент сообщает серверу, какой файл под
wwwroot/filesоткрыть. - Вызывает
OpenDocumentAsync– безсостоянийViewerсоздаёт новую сессию, сохраняет её в кэше и возвращает непрозрачный токен. - Возвращает токен как простой текст – клиент получает только токен; сервер никогда не отправляет байты документа в этом ответе.
Поскольку Viewer не хранит данные документа, эндпоинт не нуждается в блоках using, вызовах Dispose или фабриках per‑request. Токен — единственное удостоверение, которое клиент когда‑либо отправит обратно виджету просмотрщика (objViewer.View(token) на фронтенде).
Использование токена на клиентской стороне
После возврата токена фронтенд‑виджет вызывает objViewer.View(token). Затем виджет включает тот же токен в каждый последующий запрос за изображениями страниц, поиском или действиями аннотаций. Сервер проверяет токен относительно сессии, которая изначально открыла документ. Если токен представлен из другой браузерной сессии, просмотрщик возвращает ошибку авторизации, что является ожидаемым поведением безопасности.
Преждевременное закрытие сессии (опционально)
Если ваш рабочий процесс знает, что документ больше никогда не будет запрашиваться, вы можете освободить запись кэша, вызвав CloseDocument на Viewer. Это удалит сессию из кэша и предотвратит работу таймера скользящего истечения, который поддерживает её в живом состоянии.
Итоги безсостояния потока
| Step | What happens | Where state lives |
|---|---|---|
| Внедрить просмотрщик | Единый экземпляр Viewer создаётся при запуске. | В контейнере DI (singleton). |
| OpenDocumentAsync | Проверка лицензии → создание сессии → возврат токена. | Сессия хранится в IMemoryCache (или распределённом кэше). |
| Клиент рендерит | Виджет отправляет токен с каждым запросом. | Токен — единственные данные документа, которые держит клиент. |
| Истечение сессии / CloseDocument | Запись кэша удаляется после таймаута или явного закрытия. | Нет оставшихся ресурсов в сервисе просмотрщика. |
Поскольку сам сервис никогда не хранит данные документа, вы можете безопасно масштабировать ваш веб‑ферму, заменять узлы или перезапускать приложение без потери пользовательского состояния — только токен становится недействительным, заставляя клиента заново открыть документ.
Развёртывание безсостояния просмотрщика в вашем приложении
- Добавьте
AddDoconut,AddSessionи провайдер кэша вProgram.cs. - Вставьте фрагмент открытого эндпоинта в вашу конфигурацию minimal‑API.
- Передайте возвращённый токен в
objViewer.View(token)на фронтенде.
Теперь у вас есть полностью безсостояний, готовый к продакшну API открытого документа, использующий дизайн Viewer от Doconut.