How to preview content with Draft Mode in Next.js
在 Pages 文档 和 数据获取文档 中,我们讨论了如何在构建时使用 getStaticProps
和 getStaticPaths
预渲染页面(静态生成)。
当你的页面从 headless CMS 获取数据时,静态生成非常有用。然而,当你在 headless 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 路由并从浏览器手动访问来测试这一点:
// 一个简单的例子,可以从浏览器手动测试。
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('Draft mode is enabled')
}
如果你打开浏览器的开发者工具并访问 /api/draft
,你会注意到一个带有名为 __prerender_bypass
的 cookie 的 Set-Cookie
响应头。
从你的 Headless CMS 安全地访问它
实际上,你会希望从你的 headless CMS _安全地_调用这个 API 路由。具体步骤将取决于你使用的 headless CMS,但这里有一些你可以采取的常见步骤。
这些步骤假设你使用的 headless CMS 支持设置自定义草稿 URL。如果不支持,你仍然可以使用此方法来保护你的草稿 URL,但你需要手动构建和访问草稿 URL。
首先,你应该使用你选择的令牌生成器创建一个秘密令牌字符串。这个秘密只会被你的 Next.js 应用和你的 headless CMS 知道。这个秘密防止没有访问你的 CMS 权限的人访问草稿 URL。
其次,如果你的 headless 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
。
你的 headless CMS 可能允许你在草稿 URL 中包含一个变量,以便 <path>
可以根据 CMS 的数据动态设置,如:&slug=/posts/{entry.fields.slug}
最后,在草稿 API 路由中:
- 检查秘密是否匹配以及
slug
参数是否存在(如果不存在,请求应该失败)。 - 调用
res.setDraftMode
。 - 然后将浏览器重定向到
slug
指定的路径。(以下示例使用 307 重定向)。
export default async (req, res) => {
// 检查 secret 和 next 参数
// 这个 secret 应该只有这个 API 路由和 CMS 知道
if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
return res.status(401).json({ message: 'Invalid token' })
}
// 获取 headless CMS 以检查提供的 `slug` 是否存在
// getPostBySlug 将实现向 headless CMS 获取数据的必要逻辑
const post = await getPostBySlug(req.query.slug)
// 如果 slug 不存在,防止启用草稿模式
if (!post) {
return res.status(401).json({ message: 'Invalid slug' })
}
// 通过设置 cookie 启用草稿模式
res.setDraftMode({ enable: true })
// 重定向到从获取的帖子中的路径
// 我们不重定向到 req.query.slug,因为这可能导致开放重定向漏洞
res.redirect(post.slug)
}
如果成功,浏览器将被重定向到你想要查看的路径,并带有草稿模式 cookie。
步骤 2:更新 getStaticProps
下一步是更新 getStaticProps
以支持草稿模式。
如果你请求一个带有 getStaticProps
的页面,并且设置了 cookie(通过 res.setDraftMode
),那么 getStaticProps
将在请求时被调用(而不是在构建时)。
此外,它将被调用时带有一个 context
对象,其中 context.draftMode
将为 true
。
export async function getStaticProps(context) {
if (context.draftMode) {
// 动态数据
}
}
我们在草稿 API 路由中使用了 res.setDraftMode
,所以 context.draftMode
将为 true
。
如果你也使用 getStaticPaths
,那么 context.params
也将可用。
获取草稿数据
你可以更新 getStaticProps
以根据 context.draftMode
获取不同的数据。
例如,你的 headless 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)
// ...
}
就是这样!如果你从你的 headless CMS 或手动访问草稿 API 路由(带有 secret
和 slug
),你现在应该能够看到草稿内容。如果你更新你的草稿而不发布,你应该能够查看草稿。
在你的 headless 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) {
// 获取草稿数据
}
}
每次 next build
都是唯一的
每次运行 next build
时都会生成一个新的绕过 cookie 值。
这确保绕过 cookie 不能被猜测。
值得注意的是:要通过 HTTP 在本地测试草稿模式,你的浏览器需要允许第三方 cookie 和本地存储访问。