草稿模式
在页面文档和数据获取文档中,我们讨论了如何在构建时(**静态生成**)使用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 支持设置**自定义草稿 URL**。如果不支持,您仍然可以使用此方法来保护草稿 URL,但您需要手动构造和访问草稿 URL。
**首先**,您应该使用您选择的令牌生成器创建一个**秘密令牌字符串**。此秘密仅为您的 Next.js 应用程序和您的无头 CMS 所知。此秘密可以防止没有访问您 CMS 权限的人访问草稿 URL。
**其次**,如果您的无头 CMS 支持设置自定义草稿 URL,请指定以下内容作为草稿 URL。这假设您的草稿 API 路由位于pages/api/draft.ts
。
https://<your-site>/api/draft?secret=<token>&slug=<path>
<your-site>
应为您的部署域名。<token>
应替换为您生成的秘密令牌。<path>
应为要查看的页面的路径。如果要查看/posts/foo
,则应使用&slug=/posts/foo
。
您的无头 CMS 可能会允许您在草稿 URL 中包含一个变量,以便可以根据 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。
步骤 2:更新 getStaticProps
下一步是更新 getStaticProps
以支持草稿模式。
如果您请求一个页面,该页面具有设置了 Cookie 的 getStaticProps
(通过 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
默认情况下,草稿模式会话在浏览器关闭时结束。
要手动清除草稿模式 Cookie,请创建一个调用 setDraftMode({ enable: false })
的 API 路由。
export default function handler(req, res) {
res.setDraftMode({ enable: false })
}
然后,发送请求到 /api/disable-draft
以调用 API 路由。如果使用 next/link
调用此路由,则必须传递 prefetch={false}
以防止在预取时意外删除 Cookie。
适用于 getServerSideProps
草稿模式适用于 getServerSideProps
,并且在 context
对象中可用作 draftMode
键。
注意:在使用草稿模式时,不应设置
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 和本地存储访问。
这有帮助吗?