投稿のプレビューを表示する
編集者は管理画面の投稿編集画面(SCR-104)で「プレビュー」ボタンを押すと、まだ公開していない下書きを、あなたの公開サイトと同じ見た目で確認できます。このとき開くページを公開サイト側でどう用意するか——種別ごとに登録するプレビューURLの仕組み、下書きを取得する
cms.posts.getPreview()、そして静的サイト(SSG)でも成立する実行時レンダリングまでをまとめます。
get())が公開済みだけを返すのに対し、下書き版を返します。レスポンス形状は公開版と完全に同一(PostDetail)なので、公開ページの描画ロジックをそのまま再利用できます。現在、プレビューに対応するのは投稿(posts)のみです。
1. プレビュー機能とは
プレビューは「下書き(未公開)の内容を、公開したときと同じ表示で確認する」ための仕組みです。確認できるのは、未公開の新規投稿/公開済み投稿の未公開変更/変更のない公開済み投稿、のいずれも下書き版です。流れは次のとおりです。
- ① 編集者がプレビューを押す:管理画面がその投稿のプレビュー用トークン(原本・プレフィックス
pv_)を発行し、登録済みのプレビューURLに{SLUG}・{PREVIEW_TOKEN}を埋めたURLを新規タブで開きます。 - ② あなたのサイトのプレビューページが開く:そのページがクエリの
slug・tokenを読み、公開APIのプレビュー取得を呼びます。 - ③ 下書きを描画する:返ってきた下書き版を、公開詳細ページと同じ描画で表示します。
トークンの有効期限は既定で24時間です。公開サイト側でやることは、プレビューURLの登録(2章)とプレビューページの用意(3〜4章)の2つだけです。
2. プレビューURLの仕組み
プレビューURLは投稿の種別ごとに、サイト設定画面(SCR-120・管理者ロール)でテンプレートとして登録します。管理画面はこのテンプレートの中のプレースホルダを置換して、実際に開くURLを組み立てます。
{SLUG}… 対象投稿の下書き版 slug に置換されます。{PREVIEW_TOKEN}… 発行したプレビュー用トークン(原本)に置換されます。
テンプレートはあなたの公開サイト内のページを指すように設定します。例えば、お知らせ(種別 news)のプレビューページを /preview/posts/news/ に置くなら、次のように登録します(HTTPS必須。http://localhost・http://127.0.0.1 のみ例外)。
https://www.example.com/preview/posts/news/?slug={SLUG}&token={PREVIEW_TOKEN} cms.siteSettings() 等)の
post_types[].preview_url でも、各種別のテンプレートを確認できます。
3. getPreview() で下書きを取得する
下書きは cms.posts.getPreview(postType, slug, token) で取得します。内部的には
GET /api/v1/posts/{postType}/{slug}/preview を呼び、トークンは X-PREVIEW-TOKEN ヘッダで送ります(ログへのトークン露出を避けるためクエリよりヘッダが推奨です。SDKは自動でヘッダに載せます)。既存の公開API認証(X-API-KEY+X-SITE-CODE)とは別レイヤの認証で、両方を満たす必要があります。
curl -i \
-H "X-API-KEY: <APIキー>" \
-H "X-SITE-CODE: <サイトコード>" \
-H "X-PREVIEW-TOKEN: pv_8f32d7a8f9a34e2c1b0d5e6f7a8b9c0d" \
"https://api.cms.sharedan.co.jp/api/v1/posts/news/new-office-opening/preview"
SDKなら下書き版の PostDetail が返ります。トークンが不正・期限切れ・失効(投稿/サイト削除・管理者による明示失効)・対象不一致(トークンのスコープと
slug/サイトが不一致)の場合は、存在秘匿のためいずれも 404(ShareDanCmsApiError.isNotFound)に統一されます。401/403 で区別はしません。
import { ShareDanCmsApiError, renderProseMirror } from '@sharedan/cms-sdk';
import { cms } from '../lib/cms';
// プレビューURL(.../preview/posts/news/?slug=...&token=...)の
// クエリから取り出した slug / token をそのまま渡す。
async function loadPreview(postType: string, slug: string, token: string) {
try {
// 下書き版(PostDetail)。形状は公開版 get() と完全に同一。
const post = await cms.posts.getPreview(postType, slug, token);
return post.body ? renderProseMirror(post.body) : '';
} catch (err) {
if (err instanceof ShareDanCmsApiError && err.isNotFound) {
// トークンが不正/期限切れ(既定24h)/失効/対象不一致はすべて 404 に統一される。
throw new Error('プレビューURLが無効か、有効期限が切れています');
}
throw err;
}
} 4. プレビューページを作る(実行時取得)
プレビューURLのトークンは実行時にしか決まりません(編集者がボタンを押した瞬間に発行されます)。そのため、プレビューページの本文は
SSGでビルド時に焼けません。ビルド時はページの「ガワ」だけを静的出力し、本文はブラウザから実行時に
getPreview() を呼んで描画します。描画には公開詳細ページと同じレンダラ(renderProseMirror() など)を使い、下書きと公開版の見た目を揃えます。
---
// src/pages/preview/posts/news/index.astro
// CMSのプレビューURLテンプレートがこのページを指す:
// https://<あなたのサイト>/preview/posts/news/?slug={SLUG}&token={PREVIEW_TOKEN}
// token は実行時にしか決まらないため、ここでは本文を焼かずガワだけを静的出力する。
---
<html lang="ja">
<head>
<!-- プレビューは検索エンジンにインデックスさせない(必須) -->
<meta name="robots" content="noindex, nofollow" />
<title>プレビュー</title>
</head>
<body>
<div class="preview-banner">これは下書き(未公開)の内容です。</div>
<article id="preview-root">プレビューを読み込んでいます…</article>
<script>
import { ShareDanCmsClient, ShareDanCmsApiError, renderProseMirror } from '@sharedan/cms-sdk';
// 鍵はブラウザに出さない。同一オリジンのプロキシ(cms-proxy.php)が
// X-API-KEY をサーバー側で付与し、X-PREVIEW-TOKEN は上流へ透過する。
const cms = new ShareDanCmsClient({
baseUrl: `${location.origin}/cms-proxy.php`,
apiKey: 'via-proxy',
siteCode: 'via-proxy',
});
const root = document.getElementById('preview-root')!;
async function render() {
const q = new URLSearchParams(location.search);
const slug = q.get('slug');
const token = q.get('token');
if (!slug || !token) {
root.textContent = 'プレビューURLが不正です。';
return;
}
try {
const post = await cms.posts.getPreview('news', slug, token);
root.innerHTML = post.body ? renderProseMirror(post.body) : '';
if (post.title) document.title = `${post.title}(プレビュー)`;
} catch (err) {
root.textContent =
err instanceof ShareDanCmsApiError && err.isNotFound
? 'プレビューURLが無効か、有効期限が切れています。'
: 'プレビューの取得に失敗しました。';
}
}
render();
</script>
</body>
</html> cms-proxy.php)を経由し、X-API-KEY の付与はサーバー側に任せます。プロキシは X-PREVIEW-TOKEN ヘッダを上流へ透過するだけで対応できます。
5. 注意点
- キャッシュに載せない:プレビュー応答は成功・エラーとも
Cache-Control: no-storeです(下書きを変更した直後に最新を確認できるようにするため)。取得した下書きを自前のCDNやエッジにキャッシュしないでください。 - 必ず noindex:プレビューページは
<meta name="robots" content="noindex, nofollow">を付け、検索エンジンにインデックスさせないでください。解析タグ(GTM等)も載せないのが無難です。 - 返るのは下書き本文:公開版(
get())とレスポンス形状は同一ですが、中身は未公開の下書きです。statusなどの内部状態フィールドは漏らさない設計のため含まれません。 - トークンは短命・秘匿:トークンはURLに乗って編集者の手元に渡る性質上、URLを知る人は閲覧できます。有効期限(既定24時間)を過ぎる・管理者が失効させると 404 になります。ログやリファラにトークンが残らないよう、取得は
X-PREVIEW-TOKENヘッダ(SDKの既定)で行いましょう。 - 対象は投稿(posts)のみ:現在プレビュー取得APIが対応するのは投稿です。FAQ・固定コンテンツのプレビューは対象外です。
- 多言語との関係:
getPreview()は下書き(=編集中の原稿)を返すため、公開版の言語別取得(locale)とは別物で、localeは受け取りません(多言語サイトを作る)。
このページはお役に立ちましたか?
改善要望・不具合・ドキュメントの分かりにくい点など、お気軽にお寄せください。いただいた内容は今後の改善に役立てます。
お寄せいただきありがとうございます。今後の改善に役立てます。