How the Viewer Works

The document request lifecycle

Doconut renders documents as paginated images served through ASP.NET Core middleware. Understanding the lifecycle — open, token, page requests, close — explains almost every behavior you will observe, including the error messages.

The three moving parts

  • Viewer — the public service you inject. It opens documents and returns session tokens.
  • The document session — a server-side object holding the loaded document, keyed by a token in IMemoryCache.
  • The Doconut middleware — added by UseDoconut(); answers every request the browser widget makes (pages, thumbnails, search, annotations, …), always authenticated by the token.

Viewer is stateless — by design

Viewer is sealed, holds no per-request document state, and deliberately does not implement IDisposable. Sessions live independently in the session manager and are cleaned up by cache expiration or an explicit CloseDocument(token).

Inject it wherever you need it:

csharp
app.MapPost("/api/open", async (string fileName, Viewer viewer) =>
{
    var token = await viewer.OpenDocumentAsync($"files/{fileName}");
    return Results.Content(token, "text/plain");
});

What happens inside OpenDocumentAsync

  1. License gate. A rejected or version-expired license (blacklisted, tampered, or a build outside the license's update window) throws a LicenseException immediately, with the rejection reason as the message — opening never silently degrades for an invalid (as opposed to absent) license. A calendar-expired Temporary or subscription license is the exception: it does not throw — it degrades to a watermark.
  2. Session creation. The viewer factory picks the right format viewer for the file extension and loads the document (see Rendering Pipeline). The session is stored in IMemoryCache under a fresh GUID token with a sliding expirationDocOptions.TimeOut minutes, default 60. Every page request resets the clock.
  3. Security registration. With UnsafeMode = false (the default), the token is bound to the caller's ASP.NET session: a secure-{token} marker is written into the session, so only the browser session that opened the document can request its pages.
  4. The token is returned. It is the single credential for everything that follows.

The three overloads differ only in input: a file path, a file path plus a per-format config (PdfConfig, WordConfig, …), or a Stream plus a FileInfo whose extension drives format detection.

How the widget gets pages

The client widget calls the Doconut middleware with the token in the query string. What the middleware does depends on the request:

QueryPurpose
?token=…&page=NRendered page image (PNG)
?token=…&page=N&thumb=1Thumbnail
?token=…&zoom=…Zoomed page rendering
?token=…&search=termFull-text search (license-gated)
?token=…&bookmarksDocument outline/bookmarks
?token=…&copy / &showlinks / &fileFormat / &metaText copy, hyperlinks, format info, DICOM technical metadata
?token=…&action=rotate/flip/closePage actions and explicit close
?token=…&AnnSave=… / &AnnLoadSave/load annotations

Every one of these paths validates first:

  • No token → the middleware returns 404 (or a version banner when ShowDoconutInfo = true).
  • Unknown or expired token → an error image with Document session not found. Please re-open document.
  • Session middleware missing (with UnsafeMode = false) → HTTP 500 with Session middleware not configured. Call UseSession() before UseDoconut().
  • Token opened by a different browser session → an error image with You Are Not Authorized To View This Page.

Closing a document

csharp
viewer.CloseDocument(token);

CloseDocument removes the session from the cache (which disposes the underlying document engine and frees its memory immediately), deletes the secure-{token} marker, and revokes the access grant. Calling it is optional — sliding expiration does the same cleanup automatically — but for large documents it is the polite way to release memory the moment the user is done.

Takeaways

  • One open document = one session = one token. Tokens are per browser session, not global URLs.
  • The token expires on a sliding window; a viewer left idle past DocOptions.TimeOut needs a re-open.
  • Viewer can be injected and shared freely; sessions carry all the state.

Was this page helpful?