Inicio rápido
Renderiza tu primer documento en minutos
Esta guía lleva una aplicación ASP.NET Core desde un Program.cs vacío hasta un documento renderizado en el navegador: registro del servidor, el paquete completo del Viewer (barra de herramientas del Viewer, montaje del Viewer y cintas opcionales de Búsqueda/Anotación), referencias de recursos, inicialización del cliente, apertura del documento y ejecución.
Configuración del servidor
AddDoconut() registra los servicios; UseDoconutResources() y UseDoconut() conectan el middleware. La llamada a los recursos debe ser primero. Las llamadas a la sesión también son obligatorias — la seguridad de documento predeterminada de Doconut valida cada solicitud de página contra el estado de sesión de ASP.NET. ¿Ya registraste Doconut durante la Instalación? Salta a la siguiente sección.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddSession(); // Doconut document security rides on ASP.NET session state
app.UseSession(); // call UseSession() before UseDoconut()
app.UseDoconutResources(); // must be registered before UseDoconut()
app.UseDoconut();Para una disposición de rutas al estilo producción, asigna el middleware de documento a una rama explícita y mantén alineados los cuatro ajustes de ruta:
builder.Services.AddDoconut(options =>
{
options.LicensePath = Path.Combine(AppContext.BaseDirectory, "Doconut.Viewer.lic");
options.MiddlewarePath = "/doconut";
options.ResourcesPath = "/doconut-res";
options.UnsafeMode = false;
});
builder.Services.AddSession();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());MiddlewarePath es un valor de coordinación; no asigna una rama de ASP.NET Core por sí mismo. En este ejemplo el host asigna /doconut, por lo que el cliente debe usar BasePath: '/doconut'. ResourcesPath sirve el paquete incrustado en /doconut-res, y la ruta de recursos de imagen del widget es, por tanto, ResPath: '/doconut-res/images'.
Añadir el visor a una página
El Viewer es el núcleo requerido de la página. Su superficie de renderizado usa dos div anidados:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Trata la barra de herramientas, los montajes de módulos y la superficie del Viewer como una única composición de página. Búsqueda y Anotación inyectan sus cintas incrustadas en montajes opcionales, pero esos módulos nunca son independientes: siempre se adjuntan al Viewer en la misma página. Usa el mismo orden que Doconut.TestApp y Doconut.TestApp.Distributed:
<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>Referenciar los recursos del visor
En una vista Razor, el servicio Viewer inyectado emite los tags <link> y <script> del visor en orden de dependencia — el widget es un plugin de jQuery, por lo que jQuery debe cargarse antes de los scripts del visor:
@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
}))Para el paquete completo del Viewer, solicita los recursos del Viewer y de los módulos juntos:
@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 y IncludeViewerScripts son los indicadores obligatorios del núcleo. Nunca publiques un ejemplo de Cinta de Búsqueda o Anotación sin ellos, el montaje del Viewer y una instancia docViewer. ReferenceCss y ReferenceScripts omiten los recursos de un módulo opcional cuando la licencia actual no otorga esa capacidad; el Viewer central aún se inicia.
Inicializar el visor
El widget del lado del cliente es un plugin de jQuery. Este es un conjunto mínimo de opciones reales de inicialización (no pseudocódigo):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: '/doconut',
ResPath: '/doconut-res/images',
onViewerReady: function () {
// pages are visible; safe to hide a loading spinner here
},
// Forward annotation lifecycle events to the embedded ribbon when it is present.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});La capitalización de las opciones es realmente mixta — showThumbs, autoLoad y pageZoom son camelCase, pero FitType, BasePath y ResPath son PascalCase. No hay una regla consistente; si la capitalización es incorrecta la opción se ignora silenciosamente (el widget recurre a su valor predeterminado en lugar de lanzar un error).
Ensamblar el paquete completo del visor
Ambas aplicaciones de referencia .NET 8 instalan las siguientes partes juntas en una página:
| Parte del paquete | Requerimiento | Cómo se conecta |
|---|---|---|
Recursos del visor, montaje y objViewer | Requerido | Renderizador de documento central |
| Barra de herramientas del visor | Requerido en la composición de referencia | Marcado del host; los botones llaman al mismo objViewer |
| Cinta de búsqueda | Opcional, módulo con licencia | doconutSearchBar(...).attach(objViewer) |
| Cinta de anotación | Opcional, módulo con licencia | doconutAnnotationBar(...).attach(objViewer) |
Aunque la barra de herramientas principal del Viewer es marcado del host, se instala junto al Viewer y nunca debe documentarse como un control aislado. Esto mantiene su diseño, etiquetas, íconos y reglas de autorización bajo el control de tu aplicación mientras cada botón impulsa la misma instancia del Viewer:
<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>La barra de herramientas de referencia completa también copia wwwroot/js/viewerToolbar.js en la aplicación host para rotación, miniaturas, impresión, pantalla completa, diseño y ayudantes de estado de botones. Carga ese archivo host después de Viewer.ReferenceScripts(...). Mantén el ayudante y su marcado <nav id="toolbar"> juntos al copiar la implementación completa de la demo.
Mantén el orden de inicialización del paquete usado por ambas aplicaciones de referencia:
- Emitir los recursos del visor, búsqueda y anotación juntos.
- Renderizar la barra de herramientas del visor, los montajes de cintas y el montaje del visor juntos.
- Inicializar
docViewerprimero. - Crear cada cinta con licencia y adjuntarla a ese mismo
objViewer. - Abrir el documento y conservar su token para las solicitudes de los módulos.
Doconut.TestApp.Distributed mantiene esta composición UI exacta y el mismo ayudante de barra de herramientas del Viewer. Su valor de solicitud access adicional y la configuración de reintento de renderizado asíncrono pertenecen al transporte distribuido; no cambian cómo se ensamblan el Viewer, la barra de herramientas o las cintas.
Los guardias del lado del servidor son importantes: cuando una capacidad opcional no está disponible, su script no se emite, por lo que la función del plugin jQuery no existe.
<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>Ambos componentes incrustados generan su propio DOM de cinta. Búsqueda contiene los grupos Encontrar, Opciones y Resultados. Anotación contiene sus herramientas de autoría, controles de estilo, acciones de guardado y acciones opcionales de exportación/imagen. Las barras exponen open(), close(), reset() e isOpen(); siempre llama a attach(objViewer) una vez después de crearlas.
El ejemplo anterior omite callbacks opcionales del host y los puntos finales de exportación/imagen de Anotación para mantener el arranque mínimo. Consulta Búsqueda y Anotaciones para la configuración completa específica de la función, o Temas personalizados para estilizar o reemplazar la barra de herramientas del Viewer de propiedad del host.
Abrir un documento
El lado del servidor es un único endpoint: el servicio Viewer inyectado abre el documento y devuelve un token de sesión.
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/Sample.pdf");
return Results.Ok(new { token });
});El cliente recupera ese token y lo entrega al widget con objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Cerrar el documento
Llama a objViewer.Close() cuando el usuario abandona el visor o abre un documento de reemplazo. En flujos de trabajo impulsados por el servidor, viewer.CloseDocument(token) elimina inmediatamente la sesión en caché, dispone del motor de renderizado, borra su marcador de seguridad y revoca el token. La expiración deslizante eventualmente realiza la misma limpieza, pero se recomienda un cierre explícito para documentos grandes.
El flujo de solicitud completado es:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTrata el token como una credencial portadora: nunca lo registres, nunca lo persistas, entrégaselo solo al widget. Identifica una sesión de documento activa en el servidor y deja de funcionar cuando esa sesión expira — vuelve a abrir el documento para obtener uno nuevo.
Ejecutar
Coloca un PDF en wwwroot/files/Sample.pdf, ejecuta dotnet run y abre la página que aloja el widget. La primera página se renderiza en el visor, con un panel de miniaturas a la izquierda. Si no lo hace, consulta Solución de problemas.
Qué obtienes sin una licencia
Una licencia ausente no genera excepción. El visor se renderiza normalmente, pero cada página lleva una marca de agua de evaluación. Consulta Configuración de licencia para saber cómo Doconut encuentra una licencia y qué cambia una vez que lo hace.
¿Fue útil esta página?