Skip to main content

如何在 Next.js 中使用 Draft Mode 预览内容

在页面文档和数据获取文档中,我们讨论了如何使用 getStaticProps 和 getStaticPaths 在构建时预渲染页面(静态生成)。

当你的页面从无头 CMS 获取数据时,静态生成很有用。但是,当你在无头 CMS 上编写草稿并想要立即在页面上查看草稿时,这不是理想的。你希望 Next.js 在请求时而不是构建时渲染这些页面,并获取草稿内容而不是已发布的内容。你希望 Next.js 仅针对这种特定情况绕过静态生成。

Next.js 有一个称为 Draft Mode 的功能可以解决这个问题。以下是使用它的说明。

步骤 1:创建和访问 API 路由​

如果你不熟悉 Next.js API 路由,请先查看API 路由文档。

首先,创建 API 路由。它可以有任何名称 - 例如 pages/api/draft.ts

在此 API 路由中,你需要在响应对象上调用 setDraftMode。

export default function handler(req, res) {
// ...
res.setDraftMode({ enable: true })
// ...
}

这将设置一个 cookie 来启用 draft mode。包含此 cookie 的后续请求将触发 Draft Mode,改变静态生成页面的行为(稍后会详细介绍)。

你可以通过创建如下 API 路由并手动从浏览器访问它来手动测试:

pages/api/draft.ts
// 用于从浏览器手动测试的简单示例。
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('Draft mode is enabled')
}

如果你打开浏览器的开发者工具并访问 /api/draft,你会注意到一个 Set-Cookie 响应头,其中包含一个名为 __prerender_bypass 的 cookie。

从你的无头 CMS 安全访问​

在实践中,你希望从无头 CMS 安全地 调用此 API 路由。具体步骤将根据你使用的无头 CMS 而有所不同,但以下是一些你可以采取的常见步骤。

这些步骤假设你使用的无头 CMS 支持设置自定义草稿 URL。如果不支持,你仍然可以使用此方法来保护你的草稿 URL,但你需要手动构建和访问草稿 URL。

首先,你应该使用你选择的令牌生成器创建秘密令牌字符串。这个秘密只有你的 Next.js 应用程序和你的无头 CMS 知道。这个秘密可以防止没有访问你 CMS 权限的人访问草稿 URL。

其次,如果你的无头 CMS 支持设置自定义草稿 URL,请指定以下内容作为草稿 URL。这假设你的草稿 API 路由位于 pages/api/draft.ts。

Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>
  • <your-site> 应该是你的部署域名。
  • <token> 应该替换为你生成的秘密令牌。
  • <path> 应该是你想要查看的页面的路径。如果你想查看 /posts/foo,那么你应该使用 &slug=/posts/foo。

你的无头 CMS 可能允许你在草稿 URL 中包含一个变量,这样 <path> 可以根据 CMS 的数据动态设置,就像这样:&slug=/posts/{entry.fields.slug}

最后,在草稿 API 路由中:

  • 检查秘密是否匹配以及 slug 参数是否存在(如果不存在,请求应该失败)。
  • 调用 res.setDraftMode。
  • 然后将浏览器重定向到 slug 指定的路径。(以下示例使用 307 重定向)。
export default async (req, res) => {
// 检查秘密和下一个参数
// 这个秘密应该只有这个 API 路由和 CMS 知道
if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
return res.status(401).json({ message: 'Invalid token' })
}

// 获取无头 CMS 以检查提供的 `slug` 是否存在
// getPostBySlug 将实现对无头 CMS 的所需获取逻辑
const post = await getPostBySlug(req.query.slug)

// 如果 slug 不存在,阻止启用 draft mode
if (!post) {
return res.status(401).json({ message: 'Invalid slug' })
}

// 通过设置 cookie 启用 Draft Mode
res.setDraftMode({ enable: true })

// 重定向到从获取的帖子中的路径
// 我们不重定向到 req.query.slug,因为这可能导致开放重定向漏洞
res.redirect(post.slug)
}

如果成功,浏览器将被重定向到你想要查看的路径,并带有 draft mode cookie。

步骤 2:更新 getStaticProps​

下一步是更新 getStaticProps 以支持 draft mode。

如果你请求一个具有 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 获取不同的数据。

例如,你的无头 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 或手动访问,你应该能够看到草稿。

Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>

更多详情​

默认情况下,当浏览器关闭时,Draft Mode 会话结束。

要手动清除 Draft Mode cookie,请创建一个调用 setDraftMode({ enable: false }) 的 API 路由:

pages/api/disable-draft.ts
export default function handler(req, res) {
res.setDraftMode({ enable: false })
}

然后,向 /api/disable-draft 发送请求以调用 API 路由。如果使用 next/link 调用此路由,你必须传递 prefetch={false} 以防止在预取时意外删除 cookie。

与 getServerSideProps 一起工作​

Draft Mode 与 getServerSideProps 一起工作,并在 context 对象中作为 draftMode 键可用。

提示:使用 Draft Mode 时,你不应该设置 Cache-Control 头,因为它无法被绕过。相反,我们建议使用 ISR。

与 API 路由一起工作​

API 路由将可以访问请求对象上的 draftMode。例如:

export default function myApiRoute(req, res) {
if (req.draftMode) {
// 获取草稿数据
}
}

每个 next build 唯一​

每次运行 next build 时都会生成一个新的绕过 cookie 值。

这确保了绕过 cookie 无法被猜测。

提示:要在本地通过 HTTP 测试 Draft Mode,你的浏览器需要允许第三方 cookie 和本地存储访问。