Démarrage rapide
Rendez votre premier document opérationnel en quelques minutes
Ce guide vous fait passer d’une application ASP.NET Core vide Program.cs à 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.
L’application hôte doit cibler un runtime qui prend en charge netstandard2.1 — .NET Core 3.0 ou version ultérieure. Les applications de référence livrées avec cette version ciblent net8.0.
Server setup
AddDoconut() enregistre les services ; UseDoconutResources() et UseDoconut() branchent le middleware. L’appel aux ressources doit être fait en premier. Les appels de session sont également requis — la sécurité des documents 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'. Déjà enregistré Doconut lors de 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(); // appeler UseSession() avant UseDoconut()
app.UseDoconutResources(); // doit être enregistré avant UseDoconut()
app.UseDoconut();L’application de référence conserve la forme historique de la requête DocImage.axd et monte le middleware de document sur une branche correspondante. C’est l’arrangement à copier lorsque vous faites évoluer une application .NET Standard existante, car le client continue d’appeler la même URL qu’il a toujours utilisée :
builder.Services.AddDoconut(options =>
{
options.UnsafeMode = false; // conserver la vérification de session de Doconut activée
options.ShowDoconutInfo = false;
});
builder.Services.AddDistributedMemoryCache(); // l'état de session nécessite un cache (MVC/Razor Pages en ajoutent déjà un)
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(3600);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Nécessaire pour les encodages multi-octets dans certains formats de document.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseDoconutResources(); // AVANT la branche de document
app.MapWhen(
context => context.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconut());UseDoconut() ne prend aucune option dans cette version — tout a été déplacé vers AddDoconut(). MiddlewarePath est une valeur de coordination ; elle ne mappe pas une branche ASP.NET Core à elle seule. ResourcesPath sert le bundle intégré à /doconut-res, et le chemin des ressources d’image du widget est donc ResPath: 'doconut-res/images'.
Add the viewer to a 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.
<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é é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 visualiseur, demandez les ressources du visualiseur 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 visualiseur et une instance docViewer. ReferenceCss et ReferenceScripts omettent les ressources d'un module optionnel lorsque la licence actuelle ne permet pas cette capacité ; le visualiseur de base démarre quand même.
Ces appels remplacent les fichiers de script copiés à la main. Si l'application charge encore ses propres docViewer.js, documentLinks.js ou docViewer.UI.js, supprimez ces balises une fois que la page fonctionne avec les ressources émises — voir le guide de migration.
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: '/',
ResPath: 'doconut-res/images',
onViewerReady: function () {
// les pages sont visibles ; il est sûr de masquer un indicateur de chargement ici
},
// Transmettre les événements du cycle de vie des annotations au 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);
}
});Deux détails décident si cette page fonctionne du tout :
- Les rappels sont des options.
onViewerReady,onViewerBusy,onCopyetonAutoLoadStatussont passés dans l'objet d'options. Le visualiseur précédent dérivait les noms de fonctions globales à partir de l'ID du conteneur et les appelait ; celui‑ci ne le fait pas. Un gestionnaire déclaré uniquement comme fonction globale ne s'exécute jamais, et rien n'est signalé. ResPathpointe vers le préfixe des ressources, pas la racine de l'application. Avec leResourcesPathpar défaut, cette valeur estdoconut-res/images. Si vous avez modifiéResourcesPath, utilisez votre propre préfixe à la place.
Les deux échouent silencieusement — voir la section Dépannage et le guide de migration.
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 visualiseur
Les deux applications de référence installent les parties suivantes ensemble sur une même page :
| Partie du package | Exigence | Comment elle est connectée |
|---|---|---|
Ressources du visualiseur, montage et objViewer | Obligatoire | Moteur de rendu de document de base |
| Barre d'outils du visualiseur | 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 visualiseur soit du balisage hôte, elle est installée à côté du visualiseur 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 tandis que chaque bouton pilote la même instance du visualiseur :
<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>Conservez l'ordre d'initialisation du package utilisé par les deux applications de référence :
- Émettre le CSS pour le visualiseur et les modules sous licence.
- Rendre la barre d'outils du visualiseur, les montages de recherche/annotation, et le montage du visualiseur ensemble.
- Émettre les scripts pour le visualiseur et les modules sous licence.
- Charger le script d'aide de la barre d'outils propre à l'application hôte.
- Initialiser
docVieweret conserver leobjViewerrésultant. - Initialiser chaque ruban de recherche ou d'annotation sous licence.
- Appeler
attach(objViewer)sur chaque ruban. - Ouvrir le document et conserver son jeton pour les requêtes du visualiseur et des modules.
Les garde‑fous côté serveur sont importants : lorsqu'une capacité optionnelle n'est pas disponible, son script n'est pas émis, ainsi 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.
Le exemple ci‑dessus omet les rappels 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 visualiseur détenue par l'hôte.
Ouvrir un document
Le côté serveur ne comporte qu’un point de terminaison : le service Viewer injecté ouvre le document et renvoie un jeton de session. L’ouverture est asynchrone dans cette version.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// Le jeton 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);
});Il n’y a rien à préchauffer avant la première ouverture. Si vous faites évoluer une application, supprimez tout appel à InitCache() ainsi que le drapeau qui le protège — la méthode n’existe plus.
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 mise en cache, libère le moteur de rendu, efface 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 documents volumineux.
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, transmettez‑le uniquement au widget. Il identifie une session de document active sur le serveur et cesse de fonctionner lorsque cette session expire — ré‑ouvrez le document pour en obtenir un nouveau.
Exécuter
Placez un PDF dans wwwroot/files/Sample.pdf, lancez 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 cela ne se produit pas, 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. Consultez Configuration de licence pour savoir comment Doconut trouve une licence et quels changements interviennent une fois qu’elle est détectée.
Cette page était-elle utile ?