Schnellstart

Rendern Sie Ihr erstes Dokument in Minuten

Dieses Tutorial führt eine ASP.NET‑Core‑App von einer leeren Program.cs zu einem im Browser gerenderten Dokument: Server‑Registrierung, das komplette Viewer‑Paket (Viewer‑Symbolleiste, Viewer‑Einbindung und optionale Such‑/Annotations‑Bänder), Asset‑Referenzen, Client‑Initialisierung, Dokumentöffnung und Ausführung.

Servereinrichtung

AddDoconut() registriert die Dienste; UseDoconutResources() und UseDoconut() verbinden die Middleware. Der Aufruf der Ressourcen muss zuerst erfolgen. Die Sitzungsaufrufe sind ebenfalls erforderlich — Doconut‑s Standard‑Dokumentensicherheit prüft jede Seitenanfrage gegen den ASP.NET‑Sitzungszustand. Der Sitzungszustand benötigt einen Cache: AddDistributedMemoryCache() stellt einen bereit. Eine MVC‑ oder Razor‑Pages‑App registriert ihn bereits, aber eine Minimal‑API‑App tut dies nicht, und ohne ihn stoppt die Anwendung beim Start mit Unable to resolve service for type 'IDistributedCache'. Haben Sie Doconut bereits während der Installation registriert? Springen Sie zum nächsten Abschnitt.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // Sitzungszustand benötigt einen Cache (MVC/Razor Pages fügt bereits einen hinzu)
builder.Services.AddSession(); // Doconut-Dokumentensicherheit nutzt den ASP.NET‑Sitzungszustand

app.UseSession();          // Aufruf von UseSession() vor UseDoconut()
app.UseDoconutResources(); // muss vor UseDoconut() registriert werden
app.UseDoconut();

Für ein produktionsähnliches Pfadlayout ordnen Sie die Dokument‑Middleware einem expliziten Branch zu und halten die vier Pfadeinstellungen synchron:

csharp
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 ist ein Koordinationswert; er mappt keinen ASP.NET Core‑Branch von sich aus. In diesem Beispiel mappt der Host /doconut, sodass der Client BasePath: 'doconut' verwendet. ResourcesPath stellt das eingebettete Bundle unter /doconut-res bereit, und der Bild‑Ressourcen‑Pfad des Widgets lautet daher ResPath: 'doconut-res/images'. Schreiben Sie beide ohne führenden Schrägstrich: Der Viewer fügt selbst einen hinzu, sodass '/doconut' zu //doconut/DocImage.axd werden würde, was der Browser als Anfrage an einen Host namens doconut interpretiert und der Viewer mit „The viewer handler could not be reached“ fehlschlägt.

Viewer zur Seite hinzufügen

Der Viewer ist der erforderliche Kern der Seite. Seine Rendering‑Oberfläche verwendet zwei verschachtelte divs:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

Betrachten Sie die Symbolleiste, Modul‑Einbindungen und die Viewer‑Oberfläche als eine Seitenkomposition. Suche und Annotation fügen ihre eingebetteten Bänder in optionale Einbindungen ein, aber diese Module stehen niemals eigenständig: Sie werden immer an den Viewer auf derselben Seite angehängt. Verwenden Sie die gleiche Reihenfolge wie bei Doconut.TestApp und Doconut.TestApp.Distributed:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Viewer‑Navigation, Zoom, Such‑ und Annotations‑Schaltflächen -->
</nav>

<div id="searchBarMount"></div>
<div id="annBarMount"></div>

<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

Viewer‑Assets referenzieren

In einer Razor‑View gibt der injizierte Viewer‑Dienst die <link>‑ und <script>‑Tags des Viewers in Abhängigkeitsreihenfolge aus — das Widget ist ein jQuery‑Plugin, daher muss jQuery vor den Viewer‑Skripten geladen werden:

html
@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
}))

Für das komplette Viewer‑Paket fordern Sie Viewer‑ und Modul‑Ressourcen gemeinsam an:

html
@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 und IncludeViewerScripts sind die obligatorischen Kern‑Flags. Veröffentlichen Sie niemals ein Such‑ oder Annotations‑Band‑Beispiel ohne diese, die Viewer‑Einbindung und eine docViewer‑Instanz. ReferenceCss und ReferenceScripts lassen die Ressourcen eines optionalen Moduls weg, wenn die aktuelle Lizenz diese Fähigkeit nicht gewährt; der Kern‑Viewer startet dennoch.

Viewer initialisieren

Das clientseitige Widget ist ein jQuery‑Plugin. Dies ist ein minimales Set echter Initialisierungsoptionen (kein Pseudocode):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   'doconut',             // kein führender '/'
    ResPath:    'doconut-res/images',  // kein führender '/'
    onViewerReady: function () {
        // Seiten sind sichtbar; ein Lade‑Spinner kann hier sicher ausgeblendet werden
    },
    // Leite Annotations‑Lebenszyklus‑Ereignisse an das eingebettete Ribbon weiter, wenn es vorhanden ist.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

Die Schreibweise der Optionen ist tatsächlich gemischt — showThumbs, autoLoad und pageZoom sind camelCase, aber FitType, BasePath und ResPath sind PascalCase. Es gibt keine konsistente Regel; wird die Schreibweise falsch verwendet, wird die Option stillschweigend ignoriert (das Widget greift auf seine Standardeinstellung zurück, anstatt einen Fehler zu werfen).

Komplettes Viewer‑Paket zusammenstellen

Beide .NET 6‑Referenzanwendungen installieren die folgenden Teile zusammen auf einer Seite:

Teil des PaketsAnforderungWie es verbunden ist
Viewer‑Ressourcen, Einbindung und objViewerErforderlichKern‑Dokument‑Renderer
Viewer‑WerkzeugleisteErforderlich in der ReferenzzusammensetzungHost‑Markup; Buttons rufen denselben objViewer auf
Such‑RibbonOptionales, lizenziertes ModuldoconutSearchBar(...).attach(objViewer)
Annotations‑RibbonOptionales, lizenziertes ModuldoconutAnnotationBar(...).attach(objViewer)

Obwohl die Haupt‑Viewer‑Werkzeugleiste Host‑Markup ist, wird sie zusammen mit dem Viewer installiert und darf niemals als isoliertes Steuerelement dokumentiert werden. So bleiben Layout, Beschriftungen, Icons und Autorisierungsregeln unter der Kontrolle Ihrer Anwendung, während jeder Button dieselbe Viewer‑Instanz steuert:

html
<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>

Die vollständige Referenz‑Toolbar kopiert außerdem wwwroot/js/viewerToolbar.js in die Host‑Anwendung für Drehung, Miniaturansichten, Druck, Vollbild, Layout und Hilfsfunktionen für den Button‑Zustand. Laden Sie diese Host‑Datei nach Viewer.ReferenceScripts(...). Halten Sie den Helfer und sein <nav id="toolbar">‑Markup zusammen, wenn Sie die komplette Demo‑Implementierung kopieren.

Behalten Sie die Initialisierungsreihenfolge des Pakets bei, die von beiden Referenzanwendungen verwendet wird:

  1. CSS für den Viewer und lizensierte Module ausgeben.
  2. Viewer‑Werkzeugleiste, Such‑/Annotations‑Einbindungen und Viewer‑Einbindung zusammen rendern.
  3. Skripte für den Viewer und lizensierte Module ausgeben.
  4. Die viewerToolbar.js der Host‑Anwendung laden.
  5. docViewer initialisieren und das resultierende objViewer behalten.
  6. Jedes lizensierte Such‑ oder Annotations‑Ribbon initialisieren.
  7. attach(objViewer) für jedes Ribbon aufrufen.
  8. Das Dokument öffnen und sein Token für Viewer‑ und Modul‑Anfragen behalten.

Doconut.TestApp.Distributed behält diese exakte UI‑Zusammensetzung und denselben Viewer‑Toolbar‑Helper bei. Sein zusätzlicher access‑Request‑Wert und die asynchron‑Render‑Retry‑Einstellungen gehören zum verteilten Transport; sie ändern nicht, wie Viewer, Toolbar oder Ribbons zusammengebaut werden.

Die serverseitigen Schutzmechanismen sind wichtig: Wenn eine optionale Fähigkeit nicht verfügbar ist, wird ihr Skript nicht ausgegeben, sodass die jQuery‑Plug‑in‑Funktion nicht existiert.

html
<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>

Beide eingebetteten Komponenten erzeugen ihr eigenes Ribbon‑DOM. Die Suche enthält Gruppen für Finden, Optionen und Ergebnisse. Die Annotation enthält ihre Autorentools, Stil‑Steuerungen, Speicher‑Aktionen und optionale Export‑/Bild‑Aktionen. Die Leisten stellen open(), close(), reset() und isOpen() bereit; rufen Sie stets einmal nach deren Erstellung attach(objViewer) auf.

Das obige Beispiel lässt optionale Host‑Callbacks und Annotation‑Export‑/Bild‑Endpunkte weg, um den Start minimal zu halten. Siehe Suche und Annotationen für die vollständige, funktionsspezifische Einrichtung oder Benutzerdefinierte Themen, um die host‑eigene Viewer‑Toolbar zu stylen oder zu ersetzen.

Dokument öffnen

Die Serverseite besteht aus einem einzigen Endpunkt: Der injizierte Viewer‑Dienst öffnet das Dokument und gibt ein Sitzungstoken zurück.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // Das Token ist undurchsichtig — übergeben Sie es dem Widget, protokollieren oder speichern Sie es niemals.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

Der Client ruft dieses Token ab und übergibt es dem Widget mit objViewer.View(token):

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

Dokument schließen

Rufen Sie objViewer.Close() auf, wenn der Benutzer den Viewer verlässt oder ein Ersatzdokument öffnet. Bei servergesteuerten Workflows entfernt viewer.CloseDocument(token) sofort die zwischengespeicherte Sitzung, gibt die Rendering‑Engine frei, löscht deren Sicherheitsmarker und widerruft das Token. Das gleitende Ablaufdatum führt schließlich dieselbe Bereinigung durch, aber ein explizites Schließen wird bei großen Dokumenten empfohlen.

Der vollständige Anforderungsablauf ist:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> Close / CloseDocument

Behandeln Sie das Token wie ein Bearer‑Credential: Protokollieren Sie es niemals, speichern Sie es nicht, übergeben Sie es ausschließlich dem Widget. Es identifiziert eine aktive Dokumentsitzung auf dem Server und funktioniert nicht mehr, wenn diese Sitzung abläuft — öffnen Sie das Dokument erneut, um ein frisches Token zu erhalten.

Ausführen

Legen Sie ein PDF unter wwwroot/files/Sample.pdf ab, führen Sie dotnet run aus und öffnen Sie die Seite, die das Widget hostet. Die erste Seite wird im Viewer dargestellt, mit einer Miniaturansicht‑Leiste links. Sollte dies nicht der Fall sein, siehe Troubleshooting.

Was Sie ohne Lizenz erhalten

Fehlt eine Lizenz, wird kein Fehler ausgelöst. Der Viewer rendert normal, jedoch enthält jede Seite ein Evaluations‑Wasserzeichen. Siehe License Setup für Informationen darüber, wie Doconut eine Lizenz findet und welche Änderungen danach auftreten.

War diese Seite hilfreich?