Comment le Viewer fonctionne
Le cycle de vie des requêtes de documents
Doconut rend les documents sous forme d'images paginées servies via le middleware ASP.NET Core. Comprendre le cycle de vie — ouverture, jeton, requêtes de pages, fermeture — explique presque tous les comportements que vous observerez, y compris les messages d'erreur.
Les trois parties mobiles
Viewer— le service public que vous injectez. Il ouvre les documents et renvoie des jetons de session.- La session de document — un objet côté serveur contenant le document chargé, indexé par un jeton dans
IMemoryCache. - Le middleware Doconut — ajouté par
UseDoconut(); répond à chaque requête que le widget du navigateur effectue (pages,thumbnails,search,annotations, …), toujours authentifiée par le jeton.
Viewer est sans état — par conception
Viewer est scellé, ne conserve aucun état de document par requête, et ne met délibérément pas en œuvre IDisposable. Les sessions vivent indépendamment dans le gestionnaire de sessions et sont nettoyées par l'expiration du cache ou par un CloseDocument(token) explicite.
Vous ne le construisez plus. AddDoconut() l'enregistre comme service transitoire ; injectez‑le partout où vous en avez besoin :
app.MapPost("/api/open", async (string fileName, Viewer viewer) =>
{
var token = await viewer.OpenDocumentAsync($"files/{fileName}");
return Results.Content(token, "text/plain");
});Dans une application MVC, prenez‑le comme paramètre de constructeur ; dans une vue Razor, utilisez @inject Doconut.Viewer Viewer. Les caches d'instances de Viewer appartenant à l'application ne sont plus nécessaires — et ne sont plus corrects, car ils ne contiennent rien qui mérite d'être mis en cache.
Ce qui se passe à l'intérieur de OpenDocumentAsync
- Portail de licence. Une licence rejetée ou expirée (mise sur liste noire, altérée, ou une version hors de la fenêtre de mise à jour de la licence) lève immédiatement une
LicenseException, avec la raison du rejet comme message — l'ouverture ne se dégrade jamais silencieusement pour une licence invalide (par opposition à absente). Une licence temporaire ou d'abonnement expirée au niveau du calendrier constitue l'exception : elle ne lève pas d'exception — elle se dégrade en filigrane. - Création de session. La fabrique du visualiseur choisit le visualiseur de format approprié pour l'extension du fichier et charge le document (voir Pipeline de rendu). La session est stockée dans
IMemoryCachesous un nouveau jeton GUID avec une expiration glissante —DocOptions.TimeOutminutes, par défaut 60. Chaque requête de page réinitialise le compteur. - Enregistrement de sécurité. Avec
UnsafeMode = false(la valeur par défaut), le jeton est lié à la session ASP.NET de l'appelant : un marqueursecure-{token}est écrit dans la session, de sorte que seule la session du navigateur qui a ouvert le document peut demander ses pages. - Le jeton est renvoyé. C'est le seul identifiant pour tout ce qui suit.
Il n'existe aucune étape de préchauffage distincte. La bibliothèque précédente exposait InitCache() à cet effet ; elle n'existe plus, et rien ne la remplace.
Les trois surcharges ne diffèrent que par l'entrée : un chemin de fichier, un chemin de fichier plus une configuration par format (PdfConfig, WordConfig, …), ou un Stream plus un FileInfo dont l'extension détermine la détection du format.
Comment le widget obtient les pages
Le widget client appelle le middleware Doconut avec le jeton dans la chaîne de requête. Ce que fait le middleware dépend de la requête :
| Requête | Objectif |
|---|---|
?token=…&page=N | Image de page rendue (PNG) |
?token=…&page=N&thumb=1 | Miniature |
?token=…&zoom=… | Rendu de page zoomé |
?token=…&search=term | Recherche en texte intégral (filtrée par licence) |
?token=…&bookmarks | Plan du document / signets |
?token=…© / &showlinks / &fileFormat | Copie de texte, hyperliens et informations de format |
?token=…&meta | Métadonnées techniques DICOM ; renvoie 501 pour une session DICOM sur ce paquet |
?token=…&action=rotate/flip/close | Actions sur la page et fermeture explicite |
?token=…&AnnSave=… / &AnnLoad | Enregistrement / chargement des annotations |
Chacun de ces chemins valide d'abord :
- Pas de jeton → le middleware renvoie 404 (ou une bannière de version lorsque
ShowDoconutInfo = true). - Jeton inconnu ou expiré → une image d'erreur avec
Document session not found. Please re-open document. - Middleware de session manquant (avec
UnsafeMode = false) → HTTP 500 avecSession middleware not configured. Call UseSession() before UseDoconut(). - Jeton ouvert par une session de navigateur différente → une image d'erreur avec
You Are Not Authorized To View This Page.
Où le middleware est monté
UseDoconut() répond aux requêtes contenant un jeton, où que vous le montiez. L'application de référence conserve la forme historique de la requête DocImage.axd sur une branche MapWhen, de sorte qu'un client existant continue d'appeler l'URL qu'il a toujours utilisée. Quel que soit votre choix, le BasePath du client doit correspondre — voir Configuration du Viewer.
Servir les pages depuis un stockage partagé au lieu du nœud d'ouverture est une configuration totalement différente ; voir Déploiements distribués.
Fermer un document
viewer.CloseDocument(token);CloseDocument supprime la session du cache (qui libère le moteur de document sous‑jacent et libère immédiatement sa mémoire), supprime le marqueur secure-{token} et révoque l'autorisation d'accès. L'appeler est optionnel — l'expiration glissante effectue le même nettoyage automatiquement — mais pour les gros documents c'est la façon courtoise de libérer la mémoire dès que l'utilisateur a terminé.
Il est appelé sur le Viewer injecté. Le modèle précédent — construire un Viewer à courte durée de vie dans un bloc using uniquement pour fermer un jeton — n'a pas d'équivalent et n'est pas nécessaire.
Points à retenir
- Un document ouvert = une session = un jeton. Les jetons sont liés à la session du navigateur, pas à des URL globales.
- Le jeton expire selon une fenêtre glissante ; un visualiseur resté inactif au-delà de
DocOptions.TimeOutnécessite une réouverture. Viewerpeut être injecté et partagé librement ; les sessions portent tout l'état.
Cette page était-elle utile ?