Démarrage rapide
Rendez votre premier document visible en quelques minutes
Ce guide conduit une application ASP.NET Core d'un Program.cs vide à un document rendu dans le navigateur : enregistrement du serveur, le package complet du Viewer (barre d'outils du Viewer, montage du Viewer et rubans de recherche/annotation optionnels), références d'actifs, initialisation du client, ouverture du document et exécution.
Configuration du serveur
AddDoconut() enregistre les services ; UseDoconutResources() et UseDoconut() connectent le middleware. L'appel aux ressources doit être effectué en premier. Les appels de session sont également requis — la sécurité de document par défaut de Doconut valide chaque requête de page contre l'état de session ASP.NET. Déjà enregistré Doconut lors de l'Installation ? Passez à la section suivante.
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();Pour une configuration de chemins de type production, mappez le middleware de document vers une branche explicite et maintenez les quatre paramètres de chemin alignés :
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 est une valeur de coordination ; elle ne mappe pas une branche ASP.NET Core à elle seule. Dans cet exemple, l'hôte mappe /doconut, donc le client doit utiliser BasePath: '/doconut'. ResourcesPath sert le bundle intégré à /doconut-res, et le chemin des ressources d'images du widget est donc ResPath: '/doconut-res/images'.
Ajouter le visualiseur à une page
Le Viewer est le cœur requis de la page. Sa surface de rendu utilise deux div imbriqués :
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Considérez la barre d'outils, les montages de modules et la surface du Viewer comme une composition unique de page. La recherche et l'annotation injectent leurs rubans intégrés dans des montages optionnels, mais ces modules ne sont jamais autonomes : ils s'attachent toujours au Viewer sur la même page. Utilisez le même ordre que Doconut.TestApp et 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>Référencer les ressources du visualiseur
Dans une vue Razor, le service Viewer injecté génère les balises <link> et <script> du visualiseur dans l'ordre de dépendance — le widget est un plugin jQuery, donc jQuery doit être chargé avant les scripts du visualiseur :
@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
}))Pour le package complet du Viewer, demandez les ressources du Viewer et des modules ensemble :
@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 et IncludeViewerScripts sont les indicateurs de base obligatoires. Ne publiez jamais un exemple de ruban de Recherche ou d'Annotation sans eux, le montage du Viewer, et une instance docViewer. ReferenceCss et ReferenceScripts omettent les ressources d'un module optionnel lorsque la licence actuelle ne le permet pas ; le Viewer de base démarre tout de même.
Initialiser le visualiseur
Le widget côté client est un plugin jQuery. Voici un ensemble minimal d'options d'initialisation réelles (et non du pseudocode) :
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);
}
});La casse des options est réellement mixte — showThumbs, autoLoad et pageZoom sont en camelCase, mais FitType, BasePath et ResPath sont en PascalCase. Il n'existe aucune règle cohérente ; une mauvaise casse entraîne l'ignorance silencieuse de l'option (le widget revient à sa valeur par défaut au lieu de lever une exception).
Assembler le package complet du Viewer
Les deux applications de référence .NET 8 installent les parties suivantes ensemble sur une même page :
| Partie du package | Exigence | Comment c'est connecté |
|---|---|---|
Ressources du Viewer, montage, et objViewer | Obligatoire | Moteur de rendu de document principal |
| Barre d'outils du Viewer | Obligatoire dans la composition de référence | Balise hôte ; les boutons appellent le même objViewer |
| Ruban de recherche | Optionnel, module sous licence | doconutSearchBar(...).attach(objViewer) |
| Ruban d'annotation | Optionnel, module sous licence | doconutAnnotationBar(...).attach(objViewer) |
Bien que la barre d'outils principale du Viewer soit du balisage hôte, elle est installée avec le Viewer et ne doit jamais être documentée comme un contrôle isolé. Cela maintient sa mise en page, ses libellés, ses icônes et ses règles d'autorisation sous le contrôle de votre application tout en faisant en sorte que chaque bouton pilote la même instance du 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>La barre d'outils de référence complète copie également wwwroot/js/viewerToolbar.js dans l'application hôte pour les fonctions de rotation, vignette, impression, plein écran, mise en page et aides d'état des boutons. Chargez ce fichier hôte après Viewer.ReferenceScripts(...). Conservez l'aide et son balisage <nav id="toolbar"> ensemble lors de la copie de l'implémentation de démonstration complète.
Conservez l'ordre d'initialisation du package utilisé par les deux applications de référence :
- Émettre les ressources du Viewer, de la Recherche et de l'Annotation ensemble.
- Rendre la barre d'outils du Viewer, les montages des Rubans et le montage du Viewer ensemble.
- Initialiser
docVieweren premier. - Créer chaque Ruban sous licence et l'attacher à ce même
objViewer. - Ouvrir le document et conserver son jeton pour les requêtes des modules.
Doconut.TestApp.Distributed conserve cette composition d'interface exacte ainsi que la même aide de barre d'outils du Viewer. Sa valeur de requête access supplémentaire et ses paramètres de nouvelle tentative de rendu asynchrone appartiennent au transport distribué ; ils ne modifient pas la façon dont le Viewer, la barre d'outils ou les Rubans sont assemblés.
Les garde-fous côté serveur sont importants : lorsqu'une capacité optionnelle n'est pas disponible, son script n'est pas émis, donc sa fonction de plugin jQuery n'existe pas.
<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>Les deux composants intégrés génèrent leur propre DOM de Ruban. La recherche contient les groupes Trouver, Options et Résultats. L'annotation contient ses outils d'édition, contrôles de style, actions d'enregistrement et actions d'exportation/image optionnelles. Les barres exposent open(), close(), reset() et isOpen() ; appelez toujours attach(objViewer) une fois après les avoir créées.
L'exemple ci‑dessus omet les callbacks hôte optionnels et les points de terminaison d'exportation/image d'Annotation afin de garder le démarrage minimal. Voir Recherche et Annotations pour la configuration complète spécifique aux fonctionnalités, ou Thèmes personnalisés pour styliser ou remplacer la barre d'outils du Viewer détenue par l'hôte.
Ouvrir un document
Le côté serveur est un point de terminaison : le service Viewer injecté ouvre le document et renvoie un jeton de session.
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 });
});Le client récupère ce jeton et le transmet au widget avec objViewer.View(token) :
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Fermer le document
Appelez objViewer.Close() lorsque l'utilisateur quitte le visualiseur ou ouvre un document de remplacement. Dans les flux de travail pilotés par le serveur, viewer.CloseDocument(token) supprime immédiatement la session en cache, libère le moteur de rendu, supprime son marqueur de sécurité et révoque le jeton. L'expiration glissante effectue finalement le même nettoyage, mais une fermeture explicite est recommandée pour les gros documents.
Le flux de requête complet est :
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTraitez le jeton comme un credential porteur : ne le consignez jamais, ne le persistez jamais, ne le transmettez qu'au widget. Il identifie une session de document active sur le serveur et cesse de fonctionner lorsque cette session expire — rouvrez le document pour en obtenir un nouveau.
Lancer l'application
Placez un PDF dans wwwroot/files/Sample.pdf, exécutez dotnet run, et ouvrez la page qui héberge le widget. La première page s'affiche dans le visualiseur, avec un panneau de vignettes à gauche. Si ce n'est pas le cas, consultez Dépannage.
Ce que vous obtenez sans licence
Une licence manquante ne génère pas d'exception. Le visualiseur rend normalement, mais chaque page porte un filigrane d'évaluation. Voir Configuration de licence pour savoir comment Doconut trouve une licence et ce qui change une fois qu'elle est trouvée.
Cette page vous a-t-elle été utile ?