Cómo funciona el Visor

El ciclo de vida de la solicitud de documentos

Doconut renderiza documentos como imágenes paginadas servidas a través del middleware ASP.NET Core. Entender el ciclo de vida — apertura, token, solicitudes de página, cierre — explica casi todo el comportamiento que observarás, incluidos los mensajes de error.

Las tres partes móviles

  • Viewer — el servicio público que inyectas. Abre documentos y devuelve tokens de sesión.
  • La sesión del documento — un objeto del lado del servidor que mantiene el documento cargado, indexado por un token en IMemoryCache.
  • El middleware Doconut — añadido por UseDoconut(); responde a cada solicitud que hace el widget del navegador (pages, thumbnails, search, annotations, …), siempre autenticado por el token.

Viewer es sin estado — por diseño

Viewer está sellado, no mantiene estado de documento por solicitud y deliberadamente no implementa IDisposable. Las sesiones viven de forma independiente en el gestor de sesiones y se limpian mediante la expiración de la caché o mediante un CloseDocument(token) explícito.

Ya no lo construyes. AddDoconut() lo registra como un servicio transitorio; inyectalo donde lo necesites:

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

En una aplicación MVC, tómalo como un parámetro del constructor; en una vista Razor, usa @inject Doconut.Viewer Viewer. Las cachés de la aplicación que contienen instancias de Viewer ya no son necesarias — y ya no son correctas, porque no contienen nada que valga la pena cachear.

Qué ocurre dentro de OpenDocumentAsync

  1. Control de licencia. Una licencia rechazada o caducada por versión (en la lista negra, manipulada, o una compilación fuera de la ventana de actualización de la licencia) lanza inmediatamente una LicenseException, con la razón del rechazo como mensaje — la apertura nunca se degrada silenciosamente por una licencia inválida (a diferencia de una ausente). Una licencia Temporal o de suscripción caducada por calendario es la excepción: no lanza — se degrada a una marca de agua.
  2. Creación de sesión. La fábrica del visor elige el visor de formato correcto para la extensión del archivo y carga el documento (ver Rendering Pipeline). La sesión se almacena en IMemoryCache bajo un nuevo token GUID con una expiración deslizante — DocOptions.TimeOut minutos, por defecto 60. Cada solicitud de página reinicia el temporizador.
  3. Registro de seguridad. Con UnsafeMode = false (el valor predeterminado), el token está vinculado a la sesión ASP.NET del solicitante: se escribe un marcador secure-{token} en la sesión, de modo que solo la sesión del navegador que abrió el documento pueda solicitar sus páginas.
  4. Se devuelve el token. Es la única credencial para todo lo que sigue.

No hay un paso de calentamiento separado. La biblioteca anterior exponía InitCache() para ese propósito; ya no existe, y nada lo reemplaza.

Las tres sobrecargas difieren solo en la entrada: una ruta de archivo, una ruta de archivo más una configuración por formato (PdfConfig, WordConfig, …), o un Stream más un FileInfo cuya extensión determina la detección del formato.

Cómo el widget obtiene páginas

El widget cliente llama al middleware Doconut con el token en la cadena de consulta. Lo que hace el middleware depende de la solicitud:

ConsultaPropósito
?token=…&page=NImagen de página renderizada (PNG)
?token=…&page=N&thumb=1Miniatura
?token=…&zoom=…Renderizado de página con zoom
?token=…&search=termBúsqueda de texto completo (controlada por licencia)
?token=…&bookmarksEsquema del documento/marcadores
?token=…&copy / &showlinks / &fileFormatCopiar texto, hipervínculos e información de formato
?token=…&metaMetadatos técnicos DICOM; devuelve 501 para una sesión DICOM en este paquete
?token=…&action=rotate/flip/closeAcciones de página y cierre explícito
?token=…&AnnSave=… / &AnnLoadGuardar/cargar anotaciones

Cada una de estas rutas se valida primero:

  • Sin token → el middleware devuelve 404 (o un banner de versión cuando ShowDoconutInfo = true).
  • Token desconocido o expirado → una imagen de error con Document session not found. Please re-open document.
  • Falta el middleware de sesión (con UnsafeMode = false) → HTTP 500 con Session middleware not configured. Call UseSession() before UseDoconut().
  • Token abierto por una sesión de navegador diferente → una imagen de error con You Are Not Authorized To View This Page.

Dónde se monta el middleware

UseDoconut() responde a solicitudes que llevan un token, donde sea que lo montes. La aplicación de referencia mantiene la forma histórica de solicitud DocImage.axd en una rama MapWhen, de modo que un cliente existente sigue llamando a la URL que siempre utilizó. Sea lo que sea que elijas, el BasePath del cliente debe coincidir con él — ver Configuración del Visor.

Servir páginas desde almacenamiento compartido en lugar del nodo de apertura es una disposición completamente diferente; ver Despliegues Distribuidos.

Cerrando un documento

csharp
viewer.CloseDocument(token);

CloseDocument elimina la sesión de la caché (lo que dispone el motor de documento subyacente y libera su memoria inmediatamente), borra el marcador secure-{token} y revoca la concesión de acceso. Llamarlo es opcional — la expiración deslizante realiza la misma limpieza automáticamente — pero para documentos grandes es la forma educada de liberar memoria en el momento en que el usuario termina.

Se llama en el Viewer inyectado. El patrón anterior — construir un Viewer de corta vida en un bloque using únicamente para cerrar un token — no tiene equivalente y no es necesario.

Conclusiones

  • Un documento abierto = una sesión = un token. Los tokens son por sesión de navegador, no URLs globales.
  • El token expira en una ventana deslizante; un visor que permanece inactivo más allá de DocOptions.TimeOut necesita volver a abrirse.
  • Viewer puede ser inyectado y compartido libremente; las sesiones llevan todo el estado.

¿Fue útil esta página?