跳到主要内容

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

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

当你的页面从无头 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 路由(带有 secretslug),你现在应该能够看到草稿内容。如果你更新草稿而不发布,你应该能够查看草稿。

在你的无头 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 和本地存储访问。