クイックスタート
数分で最初のドキュメントをレンダリング
このハンドブックは、空の Program.cs からブラウザでドキュメントがレンダリングされるまで、ASP.NET Core アプリを段階的に構築します:サーバー登録、完全な Viewer パッケージ(Viewer ツールバー、Viewer マウント、オプションの検索/注釈リボン)、アセット参照、クライアント初期化、ドキュメントのオープン、そして実行。
サーバー設定
AddDoconut() はサービスを登録します。UseDoconutResources() と UseDoconut() はミドルウェアを接続します。リソースの呼び出しは最初に行う必要があります。セッションの呼び出しも必要です — Doconut のデフォルトドキュメントセキュリティは、すべてのページリクエストを ASP.NET セッション状態と照合して検証します。セッション状態にはキャッシュが必要で、AddDistributedMemoryCache() がそれを提供します。MVC または Razor Pages アプリはすでに登録していますが、最小限の API アプリは登録していないため、キャッシュが無いと Unable to resolve service for type 'IDistributedCache' というエラーで起動時に停止します。すでに インストール 時に Doconut を登録しましたか?次のセクションへ進んでください。
builder.Services.AddDoconut(options =>
{
options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // セッション状態にはキャッシュが必要です(MVC/Razor Pages はすでに追加されています)
builder.Services.AddSession(); // Doconut のドキュメントセキュリティは ASP.NET セッション状態に依存しています
app.UseSession(); // UseDoconut() の前に UseSession() を呼び出してください
app.UseDoconutResources(); // UseDoconut() の前に登録する必要があります
app.UseDoconut();本番環境向けのパスレイアウトでは、ドキュメントミドルウェアを明示的なブランチにマップし、4 つのパス設定を揃えておきます:
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 は調整用の値であり、単体で ASP.NET Core のブランチをマップするものではありません。この例ではホストが /doconut にマップしているため、クライアントは BasePath: 'doconut' を使用します。ResourcesPath は埋め込みバンドルを /doconut-res で提供し、ウィジェットの画像リソースパスは ResPath: 'doconut-res/images' となります。両方とも 先頭スラッシュなし で記述してください:ビューアが自動でスラッシュを付加するため、'/doconut' と書くと //doconut/DocImage.axd になり、ブラウザは doconut というホスト名へのリクエストと解釈し、ビューアは「The viewer handler could not be reached」というエラーで失敗します。
ページにビューアを追加
Viewer はページに必須のコアです。その描画領域は 2 つの入れ子になった div を使用します:
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>ツールバー、モジュールマウント、Viewer の表面を 1 つのページ構成として扱います。Search と Annotation は埋め込みリボンをオプションのマウントに注入しますが、これらのモジュールは単体では使用されません。常に同じページ上の Viewer にアタッチされます。Doconut.TestApp と Doconut.TestApp.Distributed と同じ順序で使用してください:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer のナビゲーション、ズーム、検索、注釈ボタン -->
</nav>
<div id="searchBarMount"></div>
<div id="annBarMount"></div>
<div id="divDocViewer">
<div id="div_ctlDoc"></div>
</div>ビューア資産を参照
Razor ビューでは、注入された Viewer サービスが依存順序でビューアの <link> と <script> タグを出力します — ウィジェットは jQuery プラグインなので、jQuery をビューアのスクリプトより先に読み込む必要があります:
@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
}))完全な Viewer パッケージを取得するには、Viewer とモジュールのリソースを一緒に要求します:
@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 と IncludeViewerScripts は必須のコアフラグです。Search または Annotation リボンの例をそれらなしで公開しないでください。Viewer のマウントと docViewer インスタンスが必要です。ReferenceCss と ReferenceScripts は、現在のライセンスでその機能が許可されていない場合、オプションモジュールのリソースを省略しますが、コア Viewer は引き続き起動します。
ビューアの初期化
クライアント側ウィジェットは jQuery プラグインです。これは実際の初期化オプションの最小セットです(疑似コードではありません):
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 () {
// ページが表示されているので、ここでローディングスピナーを非表示にしても安全です
},
// アノテーションのライフサイクルイベントを、存在する場合は埋め込みリボンへ転送します。
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: function (message) {
console.error('Doconut viewer error:', message);
}
});オプションのケースは実際に混在しています — showThumbs、autoLoad、pageZoom は camelCase、FitType、BasePath、ResPath は PascalCase です。一貫した規則はなく、ケースを間違えるとオプションは黙って無視され(ウィジェットは例外を投げずにデフォルトにフォールバックします)。
完全な Viewer パッケージの組み立て
.NET 6 のリファレンスアプリケーションは、以下の部品を 1 ページにまとめてインストールします:
| パッケージの部品 | 要件 | 接続方法 |
|---|---|---|
Viewer リソース、マウント、そして objViewer | 必須 | コア文書レンダラ |
| Viewer ツールバー | リファレンス構成で必須 | ホストマークアップ;ボタンは同じ objViewer を呼び出す |
| Search リボン | オプション、ライセンスモジュール | doconutSearchBar(...).attach(objViewer) |
| Annotation リボン | オプション、ライセンスモジュール | doconutAnnotationBar(...).attach(objViewer) |
メインの Viewer ツールバーはホストマークアップですが、Viewer と共にインストールされ、単独のコントロールとして文書化してはいけません。これによりレイアウト、ラベル、アイコン、認可ルールはアプリケーション側で管理でき、すべてのボタンが同じ 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>フルリファレンスツールバーは wwwroot/js/viewerToolbar.js をホストアプリケーションにコピーし、回転、サムネイル、印刷、フルスクリーン、レイアウト、ボタン状態ヘルパーを提供します。Viewer.ReferenceScripts(...) の後にそのホストファイルを読み込みます。デモ実装全体をコピーする際は、ヘルパーとその <nav id="toolbar"> マークアップを一緒に保管してください。
両方のリファレンスアプリケーションで使用されているパッケージ初期化順序を守ってください:
- Viewer とライセンスモジュール用の CSS を出力する。
- Viewer ツールバー、Search/Annotation のマウント、そして Viewer のマウントを一緒にレンダリングする。
- Viewer とライセンスモジュール用のスクリプトを出力する。
- ホストアプリケーションの
viewerToolbar.jsをロードする。 docViewerを初期化し、生成されたobjViewerを保持する。- 各ライセンスされた Search または Annotation リボンを初期化する。
- すべてのリボンで
attach(objViewer)を呼び出す。 - 文書を開き、Viewer とモジュールのリクエスト用にトークンを保持する。
Doconut.TestApp.Distributed はこの正確な UI 構成と同じ Viewer‑toolbar ヘルパーを保持しています。追加の access リクエスト値と非同期レンダー再試行設定は分散トランスポートに属し、Viewer、ツールバー、リボンの組み立て方法を変更しません。
サーバー側のガードは重要です。オプション機能が利用できない場合、そのスクリプトは出力されず、jQuery プラグイン関数も存在しません。
<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>両方の埋め込みコンポーネントはそれぞれ独自の Ribbon DOM を生成します。Search には Find、Options、Results のグループがあり、Annotation には作成ツール、スタイルコントロール、保存アクション、オプションのエクスポート/画像アクションが含まれます。各バーは open(), close(), reset(), isOpen() を公開しており、作成後は必ず一度だけ attach(objViewer) を呼び出してください。
上記の例は、起動を最小限に抑えるためにオプションのホストコールバックや Annotation のエクスポート/画像エンドポイントを省略しています。完全な機能別セットアップについては 検索 と アノテーション を参照するか、ホスト所有の Viewer ツールバーをスタイル変更または置き換える場合は カスタムテーマ をご覧ください。
ドキュメントを開く
サーバー側は 1 つのエンドポイントです。注入された Viewer サービスがドキュメントを開き、セッショントークンを返します。
app.MapPost("/api/open", async (Viewer viewer) =>
{
// トークンは不透明です — ウィジェットに渡すだけにし、ログに記録したり永続化したりしないでください。
string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
return Results.Ok(new { token });
});クライアントはそのトークンを取得し、objViewer.View(token) でウィジェットに渡します:
fetch('/api/open', { method: 'POST' })
.then(resp => resp.json())
.then(data => {
currentToken = data.token;
objViewer.View(currentToken);
});ドキュメントを閉じる
ユーザーがビューアを離れるか別のドキュメントを開くときは objViewer.Close() を呼び出します。サーバー主導のワークフローでは、viewer.CloseDocument(token) がキャッシュされたセッションを即座に削除し、レンダリングエンジンを破棄し、セキュリティマーカーを削除し、トークンを無効化します。スライディング有効期限でも最終的に同様のクリーンアップが行われますが、大きなドキュメントの場合は明示的に閉じることが推奨されます。
完了したリクエストフローは次のとおりです:
AddDoconut + middleware
-> render CSS/scripts and mount div
-> initialize docViewer
-> OpenDocumentAsync
-> return opaque token
-> objViewer.View(token)
-> page/search/annotation requests
-> Close / CloseDocumentトークンはベアラークレデンシャルとして扱い、決してログに記録せず、永続化せず、ウィジェットにのみ渡してください。トークンはサーバー上のライブドキュメントセッションを識別し、そのセッションが期限切れになると機能しなくなります — 新しいトークンを取得するにはドキュメントを再度開いてください。
実行方法
wwwroot/files/Sample.pdf に PDF を配置し、dotnet run を実行して、ウィジェットをホストするページを開きます。最初のページがビューアに表示され、左側にサムネイルパネルが表示されます。表示されない場合は、トラブルシューティング を参照してください。
ライセンスなしで得られるもの
ライセンスがない場合でも例外は発生しません。ビューアは通常通りレンダリングしますが、すべてのページに評価用の透かしが表示されます。Doconut がライセンスを取得する方法と取得後の変更点については、ライセンス設定 を参照してください。
このページは役に立ちましたか?