Menu

草稿模式

草稿模式允许你在 Next.js 应用中预览来自无头 CMS 的草稿内容。对于在构建时生成的静态页面来说这很有用,因为它允许你切换到动态渲染,并且无需重新构建整个网站就能看到草稿变更。

本页将介绍如何启用和使用草稿模式。

第 1 步:创建路由处理程序

创建一个路由处理程序。它可以使用任意名称,例如 app/api/draft/route.ts

app/api/draft/route.ts
export async function GET(request: Request) {
  return new Response('')
}
app/api/draft/route.js
export async function GET() {
  return new Response('')
}

然后,导入 draftMode 函数并调用 enable() 方法。

app/api/draft/route.ts
import { draftMode } from 'next/headers'
 
export async function GET(request: Request) {
  const draft = await draftMode()
  draft.enable()
  return new Response('草稿模式已启用')
}
app/api/draft/route.js
import { draftMode } from 'next/headers'
 
export async function GET(request) {
  const draft = await draftMode()
  draft.enable()
  return new Response('草稿模式已启用')
}

这将设置一个 cookie 来启用草稿模式。包含此 cookie 的后续请求将触发草稿模式并改变静态生成页面的行为。

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

第 2 步:从无头 CMS 访问路由处理程序

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

要从无头 CMS 安全地访问路由处理程序:

  1. 使用你选择的令牌生成器创建一个密钥令牌字符串。此密钥只有你的 Next.js 应用和无头 CMS 知道。
  2. 如果你的无头 CMS 支持设置自定义草稿 URL (假设你的路由处理程序位于 app/api/draft/route.ts),可以指定一个草稿 URL。例如:
Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>
  • <your-site> 应该是你的部署域名。
  • <token> 应该替换为你生成的密钥令牌。
  • <path> 应该是你想要查看的页面路径。如果你想查看 /posts/one,那么应该使用 &slug=/posts/one

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

  1. 在你的路由处理程序中,检查密钥是否匹配以及 slug 参数是否存在 (如果不存在,请求应该失败),调用 draftMode.enable() 来设置 cookie。然后,将浏览器重定向到 slug 指定的路径:
app/api/draft/route.ts
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')
 
  // 检查 secret 和 next 参数
  // 此密钥应该只有该路由处理程序和 CMS 知道
  if (secret !== 'MY_SECRET_TOKEN' || !slug) {
    return new Response('无效令牌', { status: 401 })
  }
 
  // 访问无头 CMS 以检查提供的 `slug` 是否存在
  // getPostBySlug 将实现所需的无头 CMS 获取逻辑
  const post = await getPostBySlug(slug)
 
  // 如果 slug 不存在则阻止启用草稿模式
  if (!post) {
    return new Response('无效的 slug', { status: 401 })
  }
 
  // 通过设置 cookie 启用草稿模式
  const draft = await draftMode()
  draft.enable()
 
  // 重定向到从获取的文章中的路径
  // 我们不重定向到 searchParams.slug,因为这可能导致开放重定向漏洞
  redirect(post.slug)
}
app/api/draft/route.js
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
 
export async function GET(request) {
  // 解析查询字符串参数
  const { searchParams } = new URL(request.url)
  const secret = searchParams.get('secret')
  const slug = searchParams.get('slug')
 
  // 检查 secret 和 next 参数
  // 此密钥应该只有该路由处理程序和 CMS 知道
  if (secret !== 'MY_SECRET_TOKEN' || !slug) {
    return new Response('无效令牌', { status: 401 })
  }
 
  // 访问无头 CMS 以检查提供的 `slug` 是否存在
  // getPostBySlug 将实现所需的无头 CMS 获取逻辑
  const post = await getPostBySlug(slug)
 
  // 如果 slug 不存在则阻止启用草稿模式
  if (!post) {
    return new Response('无效的 slug', { status: 401 })
  }
 
  // 通过设置 cookie 启用草稿模式
  const draft = await draftMode()
  draft.enable()
 
  // 重定向到从获取的文章中的路径
  // 我们不重定向到 searchParams.slug,因为这可能导致开放重定向漏洞
  redirect(post.slug)
}

如果成功,浏览器将重定向到你想要查看的路径,并带有草稿模式 cookie。

第 3 步:预览草稿内容

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

如果你请求一个设置了 cookie 的页面,那么数据将在请求时获取 (而不是在构建时)。

此外,isEnabled 的值将为 true

app/page.tsx
TypeScript
// 获取数据的页面
import { draftMode } from 'next/headers'
 
async function getData() {
  const { isEnabled } = await 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),或者手动使用 URL,你现在应该能够看到草稿内容了。而且,如果你更新草稿但不发布,你应该能够查看草稿。

下一步

查看 API 参考文档以了解更多关于草稿模式的使用方法。