跳到主要内容

草稿模式

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

¥Draft Mode allows you to preview draft content from your headless CMS in your Next.js application. This is useful for static pages that are generated at build time as it allows you to switch to dynamic rendering and see the draft changes without having to rebuild your entire site.

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

¥This page walks through how to enable and use Draft Mode.

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

¥Step 1: Create a Route Handler

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

¥Create a Route Handler. It can have any name, for example, app/api/draft/route.ts.

export async function GET(request: Request) {
return new Response('')
}
export async function GET() {
return new Response('')
}

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

¥Then, import the draftMode function and call the enable() method.

import { draftMode } from 'next/headers'

export async function GET(request: Request) {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode is enabled')
}
import { draftMode } from 'next/headers'

export async function GET(request) {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode is enabled')
}

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

¥This will set a cookie to enable draft mode. Subsequent requests containing this cookie will trigger draft mode and change the behavior of statically generated pages.

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

¥You can test this manually by visiting /api/draft and looking at your browser’s developer tools. Notice the Set-Cookie response header with a cookie named __prerender_bypass.

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

¥Step 2: Access the Route Handler from your Headless CMS

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

¥These steps assume that the headless CMS you’re using supports setting custom draft URLs. If it doesn’t, you can still use this method to secure your draft URLs, but you’ll need to construct and access the draft URL manually. The specific steps will vary depending on which headless CMS you’re using.

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

¥To securely access the Route Handler from your headless CMS:

  1. 使用你选择的令牌生成器创建一个秘密令牌字符串。这个秘密只有你的 Next.js 应用和无头 CMS 知道。

    ¥Create a secret token string using a token generator of your choice. This secret will only be known by your Next.js app and your headless CMS.

  2. 如果你的无头 CMS 支持设置自定义草稿 URL,请指定草稿 URL(假设你的路由处理程序位于 app/api/draft/route.ts)。例如:

    ¥If your headless CMS supports setting custom draft URLs, specify a draft URL (this assumes that your Route Handler is located at app/api/draft/route.ts). For example:

https://<your-site>/api/draft?secret=<token>&slug=<path>
  • <your-site> 应该是你的部署域。

    ¥<your-site> should be your deployment domain.

  • <token> 应替换为你生成的秘密令牌。

    ¥<token> should be replaced with the secret token you generated.

  • <path> 应该是你要查看的页面的路径。如果你想查看 /posts/one,那么你应该使用 &slug=/posts/one

    ¥<path> should be the path for the page that you want to view. If you want to view /posts/one, then you should use &slug=/posts/one.

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

¥Your headless CMS might allow you to include a variable in the draft URL so that <path> can be set dynamically based on the CMS’s data like so: &slug=/posts/{entry.fields.slug}

  1. 在你的路由处理程序中,检查密钥是否匹配以及 slug 参数是否存在(如果不存在,则请求应该失败),调用 draftMode.enable() 来设置 cookie。然后,将浏览器重定向到 slug 指定的路径:

    ¥In your Route Handler, check that the secret matches and that the slug parameter exists (if not, the request should fail), call draftMode.enable() to set the cookie. Then, redirect the browser to the path specified by slug:

import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
// Parse query string parameters
const { searchParams } = new URL(request.url)
const secret = searchParams.get('secret')
const slug = searchParams.get('slug')

// Check the secret and next parameters
// This secret should only be known to this Route Handler and the CMS
if (secret !== 'MY_SECRET_TOKEN' || !slug) {
return new Response('Invalid token', { status: 401 })
}

// Fetch the headless CMS to check if the provided `slug` exists
// getPostBySlug would implement the required fetching logic to the headless CMS
const post = await getPostBySlug(slug)

// If the slug doesn't exist prevent draft mode from being enabled
if (!post) {
return new Response('Invalid slug', { status: 401 })
}

// Enable Draft Mode by setting the cookie
const draft = await draftMode()
draft.enable()

// Redirect to the path from the fetched post
// We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities
redirect(post.slug)
}
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

export async function GET(request) {
// Parse query string parameters
const { searchParams } = new URL(request.url)
const secret = searchParams.get('secret')
const slug = searchParams.get('slug')

// Check the secret and next parameters
// This secret should only be known to this Route Handler and the CMS
if (secret !== 'MY_SECRET_TOKEN' || !slug) {
return new Response('Invalid token', { status: 401 })
}

// Fetch the headless CMS to check if the provided `slug` exists
// getPostBySlug would implement the required fetching logic to the headless CMS
const post = await getPostBySlug(slug)

// If the slug doesn't exist prevent draft mode from being enabled
if (!post) {
return new Response('Invalid slug', { status: 401 })
}

// Enable Draft Mode by setting the cookie
const draft = await draftMode()
draft.enable()

// Redirect to the path from the fetched post
// We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities
redirect(post.slug)
}

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

¥If it succeeds, then the browser will be redirected to the path you want to view with the draft mode cookie.

步骤 3:预览草稿内容

¥Step 3: Preview the Draft Content

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

¥The next step is to update your page to check the value of draftMode().isEnabled.

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

¥If you request a page which has the cookie set, then data will be fetched at request time (instead of at build time).

此外,isEnabled 的值将是 true

¥Furthermore, the value of isEnabled will be true.

// page that fetches data
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>
)
}
// page that fetches data
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,你现在应该能够看到草稿内容。并且,如果你在未发布的情况下更新草稿,则应该能够查看草稿。

¥If you access the draft Route Handler (with secret and slug) from your headless CMS or manually using the URL, you should now be able to see the draft content. And, if you update your draft without publishing, you should be able to view the draft.