Быстрый старт
Отобразите ваш первый документ за несколько минут
Этот пошаговый гид переводит приложение ASP.NET Core от пустого Program.cs к документу, отображаемому в браузере: регистрация сервера, полный пакет Viewer (панель инструментов Viewer, монтирование Viewer и опциональные ленты Search/Annotation), ссылки на ресурсы, инициализация клиента, открытие документа и выполнение.
Настройка сервера
AddDoconut() регистрирует сервисы; UseDoconutResources() и UseDoconut() подключают middleware. Вызов ресурсов должен быть первым. Вызовы сессии также обязательны — стандартная безопасность документов Doconut проверяет каждый запрос страницы против состояния сессии ASP.NET. Уже регистрировали Doconut во время Установка? Перейдите к следующему разделу.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddSession(); // Doconut document security rides on ASP.NET session state
app.UseSession(); // call UseSession() before UseDoconut()
app.UseDoconutResources(); // must be registered before UseDoconut()
app.UseDoconut();Для продакшн‑стиля расположения путей сопоставьте middleware документов с явной веткой и держите четыре настройки путей согласованными:
builder.Services.AddDoconut(options =>
{
options.LicensePath = Path.Combine(AppContext.BaseDirectory, "Doconut.Viewer.lic");
options.MiddlewarePath = "/doconut";
options.ResourcesPath = "/doconut-res";
options.UnsafeMode = false;
});
builder.Services.AddSession();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());MiddlewarePath — координационное значение; оно само по себе не сопоставляет ветку ASP.NET Core. В этом примере хост сопоставляет /doconut, поэтому клиент должен использовать BasePath: '/doconut'. ResourcesPath обслуживает встроенный пакет по адресу /doconut-res, а путь к изображениям виджета соответственно ResPath: '/doconut-res/images'.
Добавьте Viewer на страницу
Viewer — обязательная часть страницы. Его поверхность рендеринга использует два вложенных div:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Относитесь к панели инструментов, монтированиям модулей и поверхности Viewer как к единой композиции страницы. Search и Annotation внедряют свои встроенные ленты в опциональные монтирования, но эти модули никогда не работают автономно: они всегда присоединяются к Viewer на той же странице. Используйте тот же порядок, что и в Doconut.TestApp и Doconut.TestApp.Distributed:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer navigation, zoom, Search, and Annotation buttons -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Подключение ресурсов Viewer
В Razor‑view внедренный сервис Viewer выводит теги <link> и <script> виджета в порядке зависимостей — виджет является jQuery‑плагином, поэтому jQuery должен быть загружен до скриптов Viewer:
@inject Doconut.Viewer Viewer
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true
}))Для полного пакета Viewer запросите ресурсы Viewer и модулей одновременно:
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeBootstrapCss = true,
IncludeViewerCss = true,
IncludeSearchCss = true,
IncludeAnnotationCss = true
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeBootstrap = true,
IncludeViewerScripts = true,
IncludeSearchScripts = true,
IncludeSearchBar = true,
IncludeAnnotationScripts = true,
IncludeAnnotationBar = true
}))IncludeViewerCss и IncludeViewerScripts — обязательные базовые флаги. Никогда не публикуйте пример ленты Search или Annotation без них, без монтирования Viewer и без экземпляра docViewer. ReferenceCss и ReferenceScripts опускают ресурсы опционального модуля, если текущая лицензия не предоставляет эту возможность; ядро Viewer при этом всё равно запускается.
Инициализация Viewer
Клиентский виджет — jQuery‑плагин. Ниже минимальный набор реальных параметров инициализации (не псевдокод):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: '/doconut',
ResPath: '/doconut-res/images',
onViewerReady: function () {
// pages are visible; safe to hide a loading spinner here
},
// Forward annotation lifecycle events to the embedded ribbon when it is present.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});Регистры параметров действительно смешанные — showThumbs, autoLoad и pageZoom записаны в camelCase, а FitType, BasePath и ResPath — в PascalCase. Правил нет; если указать регистр неверно, параметр будет тихо проигнорирован (виджет вернётся к значениям по умолчанию вместо ошибки).
Сборка полного пакета Viewer
Обе справочные приложения .NET 6 устанавливают следующие части вместе на одной странице:
| Часть пакета | Требование | Как подключено |
|---|---|---|
Ресурсы Viewer, монтирование и objViewer | Обязательно | Основной рендерер документа |
| Панель инструментов Viewer | Требуется в ссылочном составе | Разметка хоста; кнопки вызывают тот же objViewer |
| Лента поиска | Опциональный, лицензированный модуль | doconutSearchBar(...).attach(objViewer) |
| Лента аннотаций | Опциональный, лицензированный модуль | doconutAnnotationBar(...).attach(objViewer) |
Хотя основная панель инструментов Viewer — разметка хоста, она устанавливается вместе с Viewer и никогда не должна документироваться как отдельный контрол. Это сохраняет её макет, подписи, иконки и правила авторизации под контролем вашего приложения, при этом каждая кнопка управляет тем же экземпляром Viewer:
<nav id="toolbar" aria-label="Document viewer controls">
<button type="button" onclick="objViewer.GotoPage(1)">First</button>
<button type="button" onclick="objViewer.Next(false)">Previous</button>
<button type="button" onclick="objViewer.Next(true)">Next</button>
<button type="button" onclick="objViewer.GotoPage(objViewer.TotalPages())">Last</button>
<button type="button" onclick="objViewer.Zoom(false)">Zoom out</button>
<button type="button" onclick="objViewer.Zoom(true)">Zoom in</button>
<button type="button" onclick="objViewer.FitType('width')">Fit width</button>
<button type="button" onclick="objViewer.FitType('height')">Fit height</button>
<button type="button" id="openSearch">Search</button>
<button type="button" id="openAnnotations">Annotations</button>
</nav>Полная справочная панель также копирует wwwroot/js/viewerToolbar.js в приложение‑хост для функций вращения, миниатюр, печати, полноэкранного режима, раскладки и управления состоянием кнопок. Загружайте этот файл после Viewer.ReferenceScripts(...). Держите помощник и его разметку <nav id="toolbar"> вместе при копировании полной демонстрационной реализации.
Соблюдайте порядок инициализации пакета, используемый в обоих справочных приложениях:
- Выпустить CSS для Viewer и лицензированных модулей.
- Отрисовать панель инструментов Viewer, монтирования Search/Annotation и монтирование Viewer вместе.
- Выпустить скрипты для Viewer и лицензированных модулей.
- Загрузить
viewerToolbar.jsиз приложения‑хоста. - Инициализировать
docViewerи сохранить полученныйobjViewer. - Инициализировать каждую лицензированную ленту поиска или аннотаций.
- Вызвать
attach(objViewer)для каждой ленты. - Открыть документ и сохранить его токен для запросов Viewer и модулей.
Doconut.TestApp.Distributed сохраняет эту точную композицию UI и тот же помощник панели Viewer. Его дополнительное значение запроса access и настройки повторных попыток асинхронного рендеринга относятся к распределённому транспорту; они не меняют способ сборки Viewer, панели инструментов или лент.
Защита на стороне сервера важна: когда опциональная возможность недоступна, её скрипт не выводится, и функция jQuery‑плагина не существует.
<script>
let currentToken = '';
const refitViewer = () =>
requestAnimationFrame(() => objViewer.Refit());
@if (Viewer.IsSearchEnabled)
{
<text>
searchBar = $('#searchBarMount').doconutSearchBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
searchBar.attach(objViewer);
</text>
}
@if (Viewer.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onLayout: refitViewer
});
annBar.attach(objViewer);
</text>
}
document.getElementById('openSearch').addEventListener('click', () => {
if (!searchBar) return;
searchBar.isOpen() ? searchBar.close() : searchBar.open();
});
document.getElementById('openAnnotations').addEventListener('click', () => {
if (!annBar) return;
annBar.isOpen() ? annBar.close() : annBar.open();
});
</script>Оба встроенных компонента генерируют собственный DOM ленты. Search содержит группы Find, Options и Results. Annotation содержит инструменты авторинга, стили, действия сохранения и опциональные действия экспорта/изображения. Бар предоставляет методы open(), close(), reset() и isOpen(); всегда вызывайте attach(objViewer) один раз после их создания.
В примере выше опущены опциональные обратные вызовы хоста и конечные точки экспорта/изображения Annotation, чтобы минимизировать старт. См. Поиск и Аннотации для полного настройки функций, либо Пользовательские темы для стилизации или замены панели Viewer, принадлежащей хосту.
Открытие документа
На стороне сервера один эндпоинт: внедренный сервис Viewer открывает документ и возвращает токен сессии.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// The token is opaque — hand it to the widget, never log or persist it.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});Клиент получает этот токен и передаёт его виджету через objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Закрытие документа
Вызовите objViewer.Close(), когда пользователь покидает просмотрщик или открывает заменяющий документ. При серверных рабочих процессах viewer.CloseDocument(token) сразу удаляет кэшированную сессию, освобождает движок рендеринга, удаляет маркер безопасности и аннулирует токен. Скользящее истечение срока действия в итоге делает то же самое, но явное закрытие рекомендуется для больших документов.
Завершённый поток запросов выглядит так:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentОтноситесь к токену как к удостоверению доступа: никогда не логируйте его, не сохраняйте, передавайте только виджету. Он идентифицирует живую сессию документа на сервере и перестаёт работать, когда сессия истекает — откройте документ заново, чтобы получить новый токен.
Запуск
Поместите PDF в wwwroot/files/Sample.pdf, запустите dotnet run и откройте страницу, где размещён виджет. Первая страница отобразится в просмотрщике с панелью миниатюр слева. Если этого не происходит, см. Устранение неполадок.
Что вы получаете без лицензии
Отсутствие лицензии не вызывает исключения. Viewer рендерится нормально, но каждая страница содержит водяной знак оценки. См. Настройка лицензии для того, как Doconut ищет лицензию и что меняется, когда она найдена.
Была ли эта страница полезной?