草稿模式
在頁面文件和資料擷取文件中,我們討論了如何使用 getStaticProps
和 getStaticPaths
在建置時預渲染頁面(靜態生成)。
當您的頁面從無頭 CMS 擷取資料時,靜態生成非常有用。然而,當您在無頭 CMS 上撰寫草稿並希望立即在頁面上檢視草稿時,這並不理想。您希望 Next.js 在請求時而不是在建置時渲染這些頁面,並擷取草稿內容而不是已發佈的內容。您希望 Next.js 僅針對這種特定情況繞過靜態生成。
Next.js 有一個稱為草稿模式的功能,可以解決這個問題。以下是使用方法的說明。
步驟 1:建立並存取 API 路由
如果您不熟悉 Next.js API 路由,請先查看API 路由文件。
首先,建立 **API 路由**。它可以是任何名稱 - 例如 `pages/api/draft.ts`
在此 API 路由中,您需要在回應物件上呼叫 `setDraftMode`。
export default function handler(req, res) {
// ...
res.setDraftMode({ enable: true })
// ...
}
這將設定一個 **cookie** 來啟用草稿模式。後續包含此 cookie 的請求將觸發 **草稿模式**,從而更改靜態生成頁面的行為(稍後會詳細說明)。
您可以透過建立如下 API 路由並從您的瀏覽器手動存取它來手動測試
// simple example for testing it manually from your browser.
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('Draft mode is enabled')
}
如果您開啟瀏覽器的開發者工具並訪問 `/api/draft`,您會注意到一個名為 `__prerender_bypass` 的 cookie 的 `Set-Cookie` 回應標頭。
從您的無頭 CMS 安全地存取它
在實務中,您會希望從您的無頭 CMS *安全地*呼叫此 API 路由。具體步驟會因您使用的無頭 CMS 而異,但以下是一些您可以採取的常見步驟。
這些步驟假設您使用的無頭 CMS 支援設定**自訂草稿網址**。如果不支援,您仍然可以使用此方法來保護您的草稿網址,但您需要手動建構和存取草稿網址。
**首先**,您應該使用您選擇的權杖產生器建立一個**秘密權杖字串**。此秘密只有您的 Next.js 應用程式和您的無頭 CMS 知道。此秘密可防止沒有存取您 CMS 權限的人存取草稿網址。
**其次**,如果您的無頭 CMS 支援設定自訂草稿網址,請將以下內容指定為草稿網址。這假設您的草稿 API 路由位於 `pages/api/draft.ts`。
https://<your-site>/api/draft?secret=<token>&slug=<path>
- `<your-site>` 應該是您的部署網域。
- `<token>` 應該替換為您產生的秘密權杖。
- `<path>` 應該是您要檢視的頁面的路徑。如果您要檢視 `/posts/foo`,則應該使用 `&slug=/posts/foo`。
您的無頭 CMS 可能允許您在草稿網址中包含一個變數,以便可以根據 CMS 的資料動態設定 `<path>`,例如:`&slug=/posts/{entry.fields.slug}`
**最後**,在草稿 API 路由中
- 檢查秘密是否相符以及 `slug` 參數是否存在(如果不存在,請求應該失敗)。
- 呼叫 `res.setDraftMode`。
- 接著將瀏覽器重新導向至
slug
指定的路徑。(以下範例使用 307 重新導向)。
export default async (req, res) => {
// Check the secret and next parameters
// This secret should only be known to this API route and the CMS
if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
return res.status(401).json({ message: 'Invalid token' })
}
// Fetch the headless CMS to check if the provided `slug` exists
// getPostBySlug would implement the required fetching logic to the headless CMS
const post = await getPostBySlug(req.query.slug)
// If the slug doesn't exist prevent draft mode from being enabled
if (!post) {
return res.status(401).json({ message: 'Invalid slug' })
}
// Enable Draft Mode by setting the cookie
res.setDraftMode({ enable: true })
// Redirect to the path from the fetched post
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
res.redirect(post.slug)
}
如果成功,瀏覽器將會重新導向至您想要使用草稿模式 cookie 查看的路徑。
步驟二:更新 getStaticProps
下一步是更新 getStaticProps
以支援草稿模式。
如果您請求一個具有 getStaticProps
且已設定 cookie(透過 res.setDraftMode
)的頁面,則 getStaticProps
將會在**請求時**被呼叫(而不是在建置時)。
此外,它會以一個 context
物件被呼叫,其中 context.draftMode
將會是 true
。
export async function getStaticProps(context) {
if (context.draftMode) {
// dynamic data
}
}
我們在草稿 API 路徑中使用了 res.setDraftMode
,因此 context.draftMode
將會是 true
。
如果您也使用 getStaticPaths
,則 context.params
也將會可用。
擷取草稿資料
您可以更新 getStaticProps
以根據 context.draftMode
擷取不同的資料。
例如,您的無頭 CMS 可能會有不同的 API 端點用於草稿文章。如果是這樣,您可以修改 API 端點 URL,如下所示
export async function getStaticProps(context) {
const url = context.draftMode
? 'https://draft.example.com'
: 'https://production.example.com'
const res = await fetch(url)
// ...
}
這樣就完成了!如果您從無頭 CMS 或手動存取草稿 API 路徑(包含 secret
和 slug
),您現在應該可以看到草稿內容。而且,如果您在未發佈的情況下更新草稿,您應該能夠查看草稿。
將此設定為您無頭 CMS 上的草稿 URL 或手動存取,您應該就能看到草稿。
https://<your-site>/api/draft?secret=<token>&slug=<path>
更多細節
清除草稿模式 cookie
然後,傳送請求到 /api/disable-draft
以叫用 API 路徑。如果使用 next/link
呼叫此路徑,您必須傳遞 prefetch={false}
以防止在預先擷取時意外刪除 cookie。
可與 getServerSideProps
搭配使用
草稿模式可與 getServerSideProps
搭配使用,並以 draftMode
鍵值在 context
物件中提供。
注意事項:使用草稿模式時,不應設定
Cache-Control
標頭,因為它無法被繞過。建議改用 ISR (增量靜態再生)。
可與 API 路由搭配使用
API 路由將可在請求物件上存取 draftMode
。例如:
export default function myApiRoute(req, res) {
if (req.draftMode) {
// get draft data
}
}
每次 next build
皆為唯一
每次執行 next build
時,都會產生新的繞過 Cookie 值。
這確保了繞過 Cookie 無法被猜測。
注意事項:要在本機透過 HTTP 測試草稿模式,您的瀏覽器需要允許第三方 Cookie 和本地儲存空間的存取。