Skip to main content

草稿模式

在从无头 CMS 获取数据的页面中,静态渲染非常有用。 然而,当您在无头 CMS 上编写草稿并希望立即在页面上查看草稿时,静态渲染并不理想。 您希望 Next.js 在请求时而不是构建时渲染这些页面,并获取草稿内容而不是已发布的内容。 您希望 Next.js 仅在这种特定情况下切换到动态渲染。

Next.js 有一个名为“草稿模式(Draft Mode)”的功能,用于解决这个问题。 以下是如何使用它的说明。

步骤 1:创建并访问路由处理程序

首先,创建一个路由处理程序。 它可以有任何名称,例如 app/api/draft/route.ts

然后,从 next/headers 中导入 draftMode 并调用 enable() 方法。

app/api/draft/route.ts
// route handler enabling draft mode
import { draftMode } from 'next/headers'

export async function GET(request: Request) {
draftMode().enable()
return new Response('Draft mode is enabled')
}

这将设置一个 cookie 以启用草稿模式。 随后包含此 cookie 的请求将触发 Draft Mode, 更改静态生成页面的行为(稍后详细说明)。

您可以通过访问 /api/draft 并查看浏览器的开发者工具来手动测试这一点。 注意 Set-Cookie 响应头带有一个名为 __prerender_bypass 的 cookie。

安全访问来自无头 CMS 的草稿模式

在实际应用中,您可能希望从无头 CMS 安全地调用此路由处理程序。 具体步骤将取决于您使用的无头 CMS,但以下是您可能采取的一些常见步骤。

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

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

其次,如果您的无头 CMS 支持设置自定义草稿 URL,请将以下内容指定为草稿 URL。 这假设您的路由处理程序位于 app/api/draft/route.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}

最后,在路由处理程序中:

  • 检查秘密是否匹配,以及是否存在 slug 参数(如果不存在,则请求应失败)。
  • 调用 draftMode.enable() 来设置 cookie。
  • 然后将浏览器重定向到 slug 指定的路径。
app/api/draft/route.ts
// route handler with secret and slug
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
// 解析查询字符串参数
const { searchParams } = new URL(request.url)
const secret = searchParams.get('secret')
const slug = searchParams.get('slug')

// 检查秘密和 next 参数
// 此秘密只应为此路由处理程序和 CMS 所知
if (secret !== 'MY_SECRET_TOKEN' || !slug) {
return new Response('Invalid token', { status: 401 })
}

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

// 如果 slug 不存在,请防止启用草稿模式
if (!post) {
return new Response('Invalid slug', { status: 401 })
}

// 通过设置 cookie 启用草稿模式
draftMode().enable()

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

如果成功,浏览器将被重定向到使用草稿模式 cookie 查看的路径。

步骤 2:更新页面

下一步是更新您的页面以检查 draftMode().isEnabled 的值。

如果请求的页面已设置了 cookie,则将在请求时(而不是在构建时)获取数据。

此外,isEnabled 的值将为 true

app/page.tsx
// page that fetches data
import { draftMode } from 'next/headers'

async function getData() {
const { isEnabled } = draftMode()

const url = isEnabled
? 'https://draft.example.com'
: 'https://production.example.com'

const res = await fetch(url)

return res.json()
}

export default async function Page() {
const { title, desc } = await getData()

return (
<main>
<h1>{title}</h1>
<p>{desc}</p>
</main>
)
}

就是这样!如果您从无头 CMS 或手动访问草稿路由处理程序(带有secretslug), 您现在应该能够看到草稿内容。如果您更新了草稿但尚未发布,您应该能够查看草稿。

将其设置为无头 CMS 上的草稿 URL 或手动访问,您应该能够看到草稿。

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

更多细节

默认情况下,草稿模式会在关闭浏览器时结束会话。

要手动清除草稿模式 cookie,请创建一个调用 draftMode().disable() 的路由处理程序:

app/api/disable-draft/route.ts
// route handler with secret and slug
import { draftMode } from 'next/headers'

export async function GET(request: Request) {
draftMode().disable()
return new Response('Draft mode is disabled')
}

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

每次 next build 时唯一的

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

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

note

要在本地通过 HTTP 测试草稿模式,您的浏览器将需要允许第三方 cookie 和本地存储访问。