Démarrage rapide
Rendez votre premier document visible en quelques minutes
Ce guide transforme une application ASP.NET Core d'un Program.cs vide en un document rendu dans le navigateur : enregistrement du serveur, le package complet du Viewer (barre d'outils du Viewer, montage du Viewer et rubans optionnels de Recherche/Annotation), les références d'actifs, l'initialisation du client, l'ouverture du document et l'exécution.
Configuration du serveur
AddDoconut() enregistre les services ; UseDoconutResources() et UseDoconut() connectent le middleware. L'appel aux ressources doit venir en premier. Les appels de session sont également requis — la sécurité documentaire par défaut de Doconut valide chaque requête de page contre l'état de session ASP.NET. L'état de session nécessite un cache : AddDistributedMemoryCache() en fournit un. Une application MVC ou Razor Pages l'enregistre déjà, mais une application minimal-API ne le fait pas, et sans cela l'application s'arrête au démarrage avec Unable to resolve service for type 'IDistributedCache'. Vous avez déjà enregistré Doconut lors de l'Installation ? Passez à la section suivante.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // l'état de session nécessite un cache (MVC/Razor Pages en ajoutent déjà un)
builder.Services.AddSession(); // La sécurité des documents Doconut repose sur l'état de session ASP.NET
app.UseSession(); // appelez UseSession() avant UseDoconut()
app.UseDoconutResources(); // doit être enregistré avant UseDoconut()
app.UseDoconut();Pour une disposition 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, "wwwroot", "Doconut.Viewer.lic");
options.MiddlewarePath = "/doconut";
options.ResourcesPath = "/doconut-res";
options.UnsafeMode = false;
});
builder.Services.AddDistributedMemoryCache();
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, de sorte que le client utilise BasePath: 'doconut'. ResourcesPath sert le bundle intégré à /doconut-res, et le chemin des ressources d'image du widget est donc ResPath: 'doconut-res/images'. Écrivez les deux sans slash initial : le visualiseur en ajoute un lui‑même, ainsi '/doconut' deviendrait //doconut/DocImage.axd, que le navigateur interprète comme une requête vers un hôte nommé doconut, et le visualiseur échoue avec "Le gestionnaire du visualiseur n'a pas pu être atteint".
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>Traitez la barre d'outils, les montages de modules et la surface du Viewer comme une seule composition de page. Recherche et
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">
<!-- Boutons de navigation, zoom, Recherche et Annotation du Viewer -->
</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é émet 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 Recherche ou 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 confère pas cette capacité ; le Viewer de base démarre tout de même.
Initialiser le Viewer
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', // pas de '/' au début
ResPath: 'doconut-res/images', // pas de '/' au début
onViewerReady: function () {
// les pages sont visibles; il est sûr de masquer un indicateur de chargement ici
},
// Transférer les événements du cycle de vie des annotations vers le ruban intégré lorsqu'il est présent.
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 ; si la casse est incorrecte, l'option est silencieusement ignorée (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 elle est connectée |
|---|---|---|
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 | Marquage 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 marquage hôte, elle est installée à côté du Viewer et ne doit jamais être documentée comme un contrôle isolé. Cela maintient sa disposition, ses libellés, ses icônes et ses règles d'autorisation sous le contrôle de votre application tandis 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 aides de rotation, vignette, impression, plein écran, mise en page et état des boutons. Chargez ce fichier hôte après Viewer.ReferenceScripts(...). Conservez le helper et son marquage <nav id="toolbar"> ensemble lors de la copie de l'implémentation de démonstration complète.
Respectez 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 du Ruban et le montage du Viewer ensemble.
- Initialiser
docVieweren premier. - Créer chaque Ruban sous licence et le rattacher à ce même
objViewer. - Ouvrir le document et conserver son jeton pour les requêtes de module.
Doconut.TestApp.Distributed conserve cette composition UI exacte ainsi que le même helper de barre d'outils du Viewer. Sa valeur de requête supplémentaire access et les 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, de sorte que 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 comprend 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 de l'Annotation afin de garder le démarrage minimal. Consultez 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 injecté Viewer ouvre le document et renvoie un jeton de session.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Le token est opaque — transmettez‑le au widget, ne le consignez ni ne le persistez jamais.
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 la 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 une information d’identification 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 le tout
Placez un PDF à 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’erreur. Le visualiseur rend normalement, mais chaque page porte un filigrane d’évaluation. Consultez Configuration de licence pour savoir comment Doconut trouve une licence et ce qui change une fois qu’elle est trouvée.
Cette page était-elle utile ?