Início rápido
Renderize seu primeiro documento em minutos
Este tutorial leva um aplicativo ASP.NET Core de um Program.cs vazio até um documento renderizado no navegador: registro no servidor, o pacote completo do Viewer (barra de ferramentas do Viewer, montagem do Viewer e fitas opcionais de Pesquisa/Anotação), referências de recursos, inicialização do cliente, abertura do documento e execução.
Configuração do servidor
AddDoconut() registra os serviços; UseDoconutResources() e UseDoconut() conectam o middleware. A chamada de recursos deve vir primeiro. As chamadas de sessão também são necessárias — a segurança de documento padrão do Doconut valida cada solicitação de página contra o estado de sessão do ASP.NET. O estado de sessão precisa de um cache: AddDistributedMemoryCache() fornece um. Um aplicativo MVC ou Razor Pages já o registra, mas um aplicativo minimal-API não, e sem ele o aplicativo para na inicialização com Unable to resolve service for type 'IDistributedCache'. Já registrou o Doconut durante a Instalação? Pule para a próxima seção.
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // o estado de sessão precisa de um cache (MVC/Razor Pages já adicionam um)
builder.Services.AddSession(); // a segurança de documento Doconut depende do estado de sessão do ASP.NET
app.UseSession(); // chame UseSession() antes de UseDoconut()
app.UseDoconutResources(); // deve ser registrado antes de UseDoconut()
app.UseDoconut();Para um layout de caminho estilo produção, mapeie o middleware de documento para um branch explícito e mantenha as quatro configurações de caminho alinhadas:
builder.Services.AddDoconut(options =>
{
options.LicensePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "Doconut.Viewer.lic");
options.MiddlewarePath = "/doconut";
options.ResourcesPath = "/doconut-res";
options.UnsafeMode = false;
});
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();
app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());MiddlewarePath é um valor de coordenação; ele não mapeia um branch do ASP.NET Core por si só. Neste exemplo o host mapeia /doconut, então o cliente usa BasePath: 'doconut'. ResourcesPath serve o pacote incorporado em /doconut-res, e o caminho de recurso de imagem do widget é, portanto, ResPath: 'doconut-res/images'. Escreva ambos sem barra inicial: o visualizador adiciona uma automaticamente, então '/doconut' se tornaria //doconut/DocImage.axd, que o navegador interpreta como uma solicitação a um host chamado doconut, e o visualizador falha com "O manipulador do visualizador não pôde ser alcançado".
Adicione o visualizador a uma página
O Viewer é o núcleo obrigatório da página. Sua superfície de renderização usa dois divs aninhados:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Trate a barra de ferramentas, as montagens de módulos e a superfície do Viewer como uma única composição de página. Pesquisa e Anotação inserem suas fitas incorporadas em montagens opcionais, mas esses módulos nunca são independentes: eles sempre se anexam ao Viewer na mesma página. Use a mesma ordem de Doconut.TestApp e Doconut.TestApp.Distributed:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Navegação do visualizador, zoom, botões de Pesquisa e Anotação -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>Referencie os recursos do visualizador
Em uma visualização Razor, o serviço Viewer injetado emite as tags <link> e <script> do visualizador em ordem de dependência — o widget é um plugin jQuery, portanto o jQuery deve ser carregado antes dos scripts do visualizador:
@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 o pacote completo do Viewer, solicite os recursos do Viewer e dos 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 e IncludeViewerScripts são as flags centrais obrigatórias. Nunca publique um exemplo de Fita de Pesquisa ou Anotação sem elas, a montagem do Viewer e uma instância docViewer. ReferenceCss e ReferenceScripts omitem os recursos de um módulo opcional quando a licença atual não concede essa capacidade; o Viewer central ainda inicia.
Inicializar o visualizador
O widget do lado do cliente é um plugin jQuery. Este é um conjunto mínimo de opções reais de inicialização (não pseudocódigo):
let searchBar = null;
let annBar = null;
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
pageZoom: 100,
FitType: 'width',
BasePath: 'doconut', // sem '/' inicial
ResPath: 'doconut-res/images', // sem '/' inicial
onViewerReady: function () {
// as páginas estão visíveis; seguro ocultar um spinner de carregamento aqui
},
// Encaminhar eventos do ciclo de vida da anotação para a faixa incorporada quando ela estiver presente.
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});A capitalização das opções é realmente misturada — showThumbs, autoLoad e pageZoom são camelCase, mas FitType, BasePath e ResPath são PascalCase. Não há regra consistente; se a capitalização estiver errada, a opção será silenciosamente ignorada (o widget volta ao seu padrão em vez de lançar um erro).
Montar o pacote completo do Visualizador
Ambas as aplicações de referência .NET 6 instalam as seguintes partes juntas em uma única página:
| Parte do pacote | Requisito | Como está conectado |
|---|---|---|
Recursos do Visualizador, montagem e objViewer | Obrigatório | Renderizador de documento principal |
| Barra de ferramentas do Visualizador | Obrigatório na composição de referência | Marcação do host; botões chamam o mesmo objViewer |
| Faixa de pesquisa | Opcional, módulo licenciado | doconutSearchBar(...).attach(objViewer) |
| Faixa de anotação | Opcional, módulo licenciado | doconutAnnotationBar(...).attach(objViewer) |
Embora a barra de ferramentas principal do Visualizador seja marcação do host, ela é instalada junto ao Visualizador e nunca deve ser documentada como um controle isolado. Isso mantém seu layout, rótulos, ícones e regras de autorização sob o controle da sua aplicação, enquanto cada botão aciona a mesma instância do Visualizador:
<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>A barra de ferramentas completa de referência também copia wwwroot/js/viewerToolbar.js para a aplicação host para rotação, miniaturas, impressão, tela cheia, layout e auxiliares de estado dos botões. Carregue esse arquivo host após Viewer.ReferenceScripts(...). Mantenha o auxiliar e sua marcação <nav id="toolbar"> juntos ao copiar a implementação completa de demonstração.
Mantenha a ordem de inicialização do pacote usada por ambas as aplicações de referência:
- Emita CSS para o Visualizador e módulos licenciados.
- Renderize a barra de ferramentas do Visualizador, as montagens de Pesquisa/Anotação e a montagem do Visualizador juntos.
- Emita scripts para o Visualizador e módulos licenciados.
- Carregue o
viewerToolbar.jsda aplicação host. - Inicialize
docViewere mantenha oobjViewerresultante. - Inicialize cada Faixa de Pesquisa ou Anotação licenciada.
- Chame
attach(objViewer)em cada Faixa. - Abra o documento e retenha seu token para solicitações do Visualizador e dos módulos.
Doconut.TestApp.Distributed mantém esta composição de UI exata e o mesmo auxiliar da barra de ferramentas do Visualizador. Seu valor adicional de requisição access e as configurações de tentativa de renderização assíncrona pertencem ao transporte distribuído; eles não alteram como o Visualizador, a barra de ferramentas ou as Faixas são montados.
As proteções do lado do servidor são importantes: quando uma capacidade opcional não está disponível, seu script não é emitido, portanto sua função de plug-in jQuery não 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 os componentes incorporados geram seu próprio DOM de Faixa. A Pesquisa contém os grupos Encontrar, Opções e Resultados. A Anotação contém suas ferramentas de autoria, controles de estilo, ações de salvamento e ações opcionais de exportação/imagem. As faixas expõem open(), close(), reset() e isOpen(); sempre chame attach(objViewer) uma vez após criá‑las.
O exemplo acima omite callbacks opcionais do host e endpoints de exportação/imagem da Anotação para manter a inicialização mínima. Veja Pesquisa e Anotações para a configuração completa de recursos específicos, ou Temas Personalizados para estilizar ou substituir a barra de ferramentas do Visualizador controlada pelo host.
Abrir um documento
O lado do servidor tem um único endpoint: o serviço injetado Viewer abre o documento e devolve um token de sessão.
app.MapPost("/api/open", async (Viewer viewer) =>
{
// O token é opaco — passe‑o para o widget, nunca registre ou persista‑o.
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});O cliente obtém esse token e o entrega ao widget com objViewer.View(token):
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});Fechar o documento
Chame objViewer.Close() quando o usuário sair do visualizador ou abrir um documento de substituição. Em fluxos controlados pelo servidor, viewer.CloseDocument(token) remove imediatamente a sessão em cache, libera o motor de renderização, exclui seu marcador de segurança e revoga o token. A expiração deslizante realiza a mesma limpeza eventualmente, mas o fechamento explícito é recomendado para documentos grandes.
O fluxo de requisição concluído é:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentTrate o token como uma credencial de portador: nunca registre, nunca persista, entregue‑o apenas ao widget. Ele identifica uma sessão de documento ativa no servidor e deixa de funcionar quando essa sessão expira — reabra o documento para obter um novo.
Executar
Coloque um PDF em wwwroot/files/Sample.pdf, execute dotnet run e abra a página que hospeda o widget. A primeira página é renderizada no visualizador, com um painel de miniaturas à esquerda. Se isso não acontecer, consulte Solução de Problemas.
O que você obtém sem uma licença
A falta de licença não gera erro. O visualizador renderiza normalmente, mas cada página exibe uma marca d'água de avaliação. Veja Configuração de Licença para saber como o Doconut encontra uma licença e o que muda quando isso ocorre.
Esta página foi útil?