草稿模式
在 Pages 文档和 数据获取文档中,我们讨论了如何使用 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 路由并从浏览器手动访问来测试:
// 用于从浏览器手动测试的简单示例。
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('草稿模式已启用')
}
如果打开浏览器的开发者工具并访问 /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://<你的站点>/api/draft?secret=<令牌>&slug=<路径>
<你的站点>
应该是你的部署域名。<令牌>
应替换为你生成的秘密令牌。<路径>
应该是你想查看的页面路径。如果你想查看/posts/foo
,则应使用&slug=/posts/foo
。
你的无头 CMS 可能允许你在草稿 URL 中包含变量,以便 <路径>
可以根据 CMS 的数据动态设置,如:&slug=/posts/{entry.fields.slug}
最后,在草稿 API 路由中:
- 检查秘密是否匹配,并且
slug
参数是否存在(如果不存在,请求应该失败)。 - 调用
res.setDraftMode
。 - 然后将浏览器重定向到
slug
指定的路径。(下面的示例使用 307 重定向)。
export default async (req, res) => {
// 检查秘密和 next 参数
// 这个秘密应该只有此 API 路由和 CMS 知道
if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
return res.status(401).json({ message: '无效令牌' })
}
// 获取无头 CMS 以检查提供的 `slug` 是否存在
// getPostBySlug 将实现获取无头 CMS 所需的获取逻辑
const post = await getPostBySlug(req.query.slug)
// 如果 slug 不存在,阻止启用草稿模式
if (!post) {
return res.status(401).json({ message: '无效的 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
也将可用。
获取草稿数据
你可以根据 context.draftMode
更新 getStaticProps
以获取不同的数据。
例如,你的无头 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://<你的站点>/api/draft?secret=<令牌>&slug=<路径>
更多细节
清除草稿模式 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 和本地存储访问。