
Build a Secure Document Management Viewer in .NET with Doconut
Implementing Document Management with Doconut
A document management workflow in a .NET portal usually needs three things:
- A way to load PDFs, Office files, CAD drawings, or images without writing a viewer from scratch.
- A secure token that ties the loaded document to the current browser session.
- A clean‑up step that frees memory as soon as the user is done.
Doconut provides all three through a single Viewer service, a lightweight IMemoryCache session, and explicit CloseDocument calls.
1. Register the viewer in the DI container
During application startup, add Doconut services to the DI container by calling the Doconut registration method described in the official documentation. This step registers the viewer, license handling, and the in‑memory cache that will hold document sessions.
The registration validates the license at startup, so any missing or invalid license fails fast and you know the problem before the first request hits the server.
2. Insert the middleware that serves page images, thumbnails, and annotation data
Configure the Doconut middleware in the request pipeline so that any request containing a ?token= query string is handled by Doconut. The middleware enforces the default security marker (secure-{token}) when UnsafeMode is false (the default) and serves page images, thumbnails, and annotation data.
Placing the middleware on a dedicated branch keeps the rest of your pipeline untouched and allows horizontal scaling without sticky sessions.
3. Open a document and obtain the opaque session token
Create a single endpoint that receives a file path (or a stream) and returns the token. The token is the only credential the client widget ever sees.
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/Contract.pdf");
return Results.Ok(new { token });
});
What happens inside OpenDocumentAsync
- License gate – an invalid license throws
LicenseExceptionimmediately. - Session creation – Doconut picks the right format viewer (PDF, Word, DWG, …) and stores the engine in
IMemoryCacheunder a GUID token. - Security registration – with
UnsafeMode = falsethe token is bound to the caller’s ASP.NET session via asecure-{token}marker. Only that browser session can request pages. - Token return – the client receives the token and uses it for every subsequent request (
/DocImage.axd?token=…).
The default sliding expiration is 60 minutes (configurable via DocOptions.TimeOut). Each page request resets the clock, so idle sessions disappear automatically.
4. Render the document on the client side
From the browser you call the endpoint, grab the token, and hand it to the Doconut widget:
fetch('/api/open', { method: 'POST' })
.then(r => r.json())
.then(d => {
const token = d.token;
objViewer.View(token); // JavaScript widget supplied by Doconut
});
The widget internally issues page, thumbnail, search, and annotation requests that all include the same token. Because the token is tied to the server‑side session, a malicious user cannot hijack another user’s document.
5. Close the document as soon as the user leaves the viewer
For large PDFs or CAD files you should release the rendering engine immediately. Call CloseDocument from a server‑side endpoint or from the widget’s “unload” event.
// Server‑side endpoint
app.MapPost("/api/close", (Viewer viewer, string token) =>
{
viewer.CloseDocument(token);
return Results.Ok();
});
// Direct call from the widget (if you expose the token to the backend)
viewer.CloseDocument(token);
CloseDocument removes the session from the cache, disposes the underlying document engine, deletes the secure-{token} marker, and revokes the access grant. Sliding expiration would eventually do the same, but an explicit close guarantees that memory is reclaimed the instant the user is done.
6. Optional: Convert a document on the server
If your workflow requires a PDF export of an uploaded Word file, enable the Converter plugin in the Doconut configuration (see documentation). After the plugin is registered you can inject DocumentConverter and call its ConvertAsync method. The conversion runs on the server, so you never need Office installed on the web server.
7. Scaling considerations for enterprise‑grade document management
- Stateless front‑ends – The viewer widget never holds document data; all heavy lifting stays on the server.
- Distributed cache – Replace the default in‑memory cache with a distributed provider (Redis, NCache) if you run multiple web‑farm nodes. The token‑to‑session mapping works unchanged because the middleware reads from the configured
IMemoryCache. - Background jobs – You can pre‑open documents in a background worker, cache the token, and hand it to the UI later. Just remember that each token is single‑use per session; reuse requires a fresh
OpenDocumentAsynccall.
These patterns let you build a document management platform that scales from a single‑server intranet portal to a multi‑region cloud service without rewriting the viewer logic.
8. Quick checklist for a production‑ready implementation
| Step | Action |
|---|---|
| 1 | Register Doconut services in Program.cs (validate license). |
| 2 | Add Doconut middleware on a dedicated branch. |
| 3 | Expose an /api/open endpoint that calls OpenDocumentAsync. |
| 4 | Pass the returned token to objViewer.View(token) in the client. |
| 5 | Call CloseDocument(token) when the viewer is hidden or the page unloads. |
| 6 | (Optional) Enable ConverterPlugin if you need server‑side conversion. |
| 7 | (Optional) Swap IMemoryCache for a distributed cache for horizontal scaling. |
Follow the list, and you have a complete, secure, high‑performance document management flow built on Doconut’s native .NET viewer.