Режим черновика (Draft Mode)
В документации по страницам (Pages) и документации по получению данных (Data Fetching) мы обсуждали, как предварительно отрендерить страницу во время сборки (Статическая генерация) с помощью getStaticProps
и getStaticPaths
.
Статическая генерация полезна, когда ваши страницы получают данные из headless CMS. Однако это не идеально, когда вы пишете черновик в вашей headless CMS и хотите сразу просмотреть его на странице. В этом случае вам нужно, чтобы Next.js рендерил эти страницы во время запроса, а не во время сборки, и загружал черновик вместо опубликованного контента. Вам нужно, чтобы Next.js обходил статическую генерацию только для этого конкретного случая.
Next.js предоставляет функцию Режим черновика (Draft Mode), которая решает эту проблему. Вот инструкции по её использованию.
Шаг 1: Создание и доступ к API-маршруту
Сначала ознакомьтесь с документацией по API-маршрутам, если вы не знакомы с API-маршрутами Next.js.
Сначала создайте API-маршрут. Он может иметь любое имя, например pages/api/draft.ts
.
В этом API-маршруте вам нужно вызвать setDraftMode
для объекта ответа.
export default function handler(req, res) {
// ...
res.setDraftMode({ enable: true })
// ...
}
Это установит cookie для включения режима черновика. Последующие запросы, содержащие этот cookie, будут активировать Режим черновика, изменяя поведение для статически сгенерированных страниц (подробнее об этом позже).
Вы можете протестировать это вручную, создав API-маршрут, как показано ниже, и обратившись к нему вручную из браузера:
// Простой пример для ручного тестирования из браузера.
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('Режим черновика включен')
}
Если вы откроете инструменты разработчика в браузере и перейдёте по адресу /api/draft
, вы заметите заголовок ответа Set-Cookie
с cookie под названием __prerender_bypass
.
Безопасный доступ из вашей Headless CMS
На практике вам нужно вызывать этот API-маршрут безопасно из вашей headless CMS. Конкретные шаги будут зависеть от используемой headless CMS, но вот некоторые общие действия.
Эти шаги предполагают, что ваша headless CMS поддерживает настройку пользовательских URL черновиков. Если нет, вы всё равно можете использовать этот метод для защиты URL черновиков, но вам нужно будет создавать и обращаться к URL черновика вручную.
Во-первых, создайте секретный токен с помощью генератора токенов. Этот секрет будет известен только вашему приложению Next.js и вашей headless CMS. Это предотвратит доступ к URL черновиков для тех, у кого нет доступа к CMS.
Во-вторых, если ваша headless CMS поддерживает настройку пользовательских URL черновиков, укажите следующий URL в качестве URL черновика. Предполагается, что ваш API-маршрут черновика находится по адресу pages/api/draft.ts
.
https://<ваш-сайт>/api/draft?secret=<токен>&slug=<путь>
<ваш-сайт>
должен быть вашим доменом развёртывания.<токен>
должен быть заменён на сгенерированный секретный токен.<путь>
должен быть путём к странице, которую вы хотите просмотреть. Если вы хотите просмотреть/posts/foo
, используйте&slug=/posts/foo
.
Ваша headless CMS может позволять включать переменную в URL черновика, чтобы <путь>
мог быть установлен динамически на основе данных 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: 'Неверный токен' })
}
// Запрос к headless CMS для проверки существования указанного `slug`
// getPostBySlug реализует необходимую логику запроса к headless CMS
const post = await getPostBySlug(req.query.slug)
// Если slug не существует, предотвращаем включение режима черновика
if (!post) {
return res.status(401).json({ message: 'Неверный slug' })
}
// Включаем режим черновика, устанавливая cookie
res.setDraftMode({ enable: true })
// Перенаправляем на путь из полученного поста
// Мы не перенаправляем на req.query.slug, чтобы избежать уязвимостей открытого перенаправления
res.redirect(post.slug)
}
Если всё успешно, браузер будет перенаправлен на путь, который вы хотите просмотреть, с cookie режима черновика.
Шаг 2: Обновление getStaticProps
Следующий шаг — обновить getStaticProps
для поддержки режима черновика.
Если вы запрашиваете страницу, которая использует getStaticProps
, с установленным cookie (через res.setDraftMode
), то getStaticProps
будет вызван во время запроса (а не во время сборки).
Кроме того, он будет вызван с объектом context
, где context.draftMode
будет true
.
export async function getStaticProps(context) {
if (context.draftMode) {
// динамические данные
}
}
Мы использовали res.setDraftMode
в API-маршруте черновика, поэтому context.draftMode
будет true
.
Если вы также используете getStaticPaths
, то context.params
также будет доступен.
Загрузка данных черновика
Вы можете обновить getStaticProps
для загрузки разных данных в зависимости от context.draftMode
.
Например, ваша headless CMS может иметь другой API-эндпоинт для черновиков постов. В этом случае вы можете изменить URL API-эндпоинта следующим образом:
export async function getStaticProps(context) {
const url = context.draftMode
? 'https://draft.example.com'
: 'https://production.example.com'
const res = await fetch(url)
// ...
}
Всё готово! Если вы обращаетесь к API-маршруту черновика (с secret
и slug
) из вашей headless CMS или вручную, вы теперь сможете увидеть черновик. И если вы обновите черновик без публикации, вы сможете просмотреть изменения.
Установите этот URL в качестве URL черновика в вашей headless CMS или обращайтесь вручную, и вы сможете видеть черновик.
https://<ваш-сайт>/api/draft?secret=<токен>&slug=<путь>
Дополнительные детали
Очистка cookie режима черновика
По умолчанию сессия режима черновика завершается при закрытии браузера.
Чтобы очистить cookie режима черновика вручную, создайте API-маршрут, который вызывает setDraftMode({ enable: false })
:
export default function handler(req, res) {
res.setDraftMode({ enable: false })
}
Затем отправьте запрос к /api/disable-draft
для вызова API-маршрута. Если вы вызываете этот маршрут с помощью next/link
, вы должны передать prefetch={false}
, чтобы случайно не удалить cookie при предварительной загрузке.
Совместимость с getServerSideProps
Режим черновика работает с getServerSideProps
и доступен как ключ draftMode
в объекте context
.
Полезно знать: Не следует устанавливать заголовок
Cache-Control
при использовании режима черновика, так как его нельзя обойти. Вместо этого мы рекомендуем использовать ISR.
Совместимость с API-маршрутами
API-маршруты будут иметь доступ к draftMode
в объекте запроса. Например:
export default function myApiRoute(req, res) {
if (req.draftMode) {
// получение данных черновика
}
}
Уникальность для каждого next build
Новое значение cookie для обхода будет генерироваться каждый раз при запуске next build
.
Это гарантирует, что cookie для обхода нельзя угадать.
Полезно знать: Для тестирования режима черновика локально через HTTP ваш браузер должен разрешать сторонние cookie и доступ к локальному хранилищу.