JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Работа с Request и Response:

Работа с Request и Response: request.json(), headers, method

Модуль 4: Node.js, Next.js и Angular
12 уровень , 2 лекция
Открыта

1. Введение

Когда пользователь или клиентское приложение отправляет запрос к вашему API, Next.js передаёт в ваш обработчик объект запроса — Request. Это не тот же самый объект, что в Node.js или Express, а современный объект, основанный на стандартном Web API Request. Аналогично, для ответа вы используете объект Response или, чаще, NextResponse.

Почему это важно?
Потому что теперь вы работаете с API, максимально похожим на браузерный fetch, а не на старый добрый Express. Это делает код переносимым и современным, но иногда добавляет неожиданностей, если вы привыкли к Node.js.

Объект Request: что это такое и зачем нужен

В Route Handlers ваш обработчик получает первым аргументом объект Request. Это инкапсулированная информация о входящем запросе: метод, заголовки, путь, тело, параметры и т.д.

Пример обработчика:

export async function POST(request) {
  // здесь request — это объект Request
}

Основные свойства и методы объекта Request

Свойство/метод Описание
request.method HTTP-метод запроса (GET, POST, и т.д.)
request.headers Коллекция заголовков запроса
request.url Полный URL запроса
request.json() Асинхронно парсит тело запроса как JSON
request.text() Асинхронно возвращает тело запроса как строку
request.formData() Асинхронно парсит тело как FormData (для форм)
request.body ReadableStream для низкоуровневого чтения тела

Важно: почти все методы работы с телом запроса (json(), text(), formData()) — асинхронные и могут быть вызваны только ОДИН раз за обработку!

2. Свойства объекта request

request.method: определяем тип запроса

Самое первое, что обычно проверяют в API-обработчике — это метод запроса. Например, чтобы разрешить только POST-запросы, а остальные отвергать.

export async function handler(request) {
  if (request.method !== 'POST') {
    return new Response('Method Not Allowed', { status: 405 });
  }
  // ...дальше логика для POST
}

Зачем это нужно?
API-эндпоинт может быть вызван разными методами, но не все из них должны быть разрешены. Например, на эндпоинт для создания сущности обычно разрешён только POST.

request.headers: работа с заголовками

Заголовки — это метаданные запроса: тип содержимого (Content-Type), авторизация (Authorization), пользовательский агент (User-Agent), и многое другое.

Объект request.headers — это не обычный объект, а экземпляр класса Headers. У него есть методы для получения значений:

export async function POST(request) {
  const contentType = request.headers.get('content-type');
  const userAgent = request.headers.get('user-agent');
  // ...
}
  • Названия заголовков нечувствительны к регистру (но лучше писать в нижнем).
  • Если заголовок не найден, возвращается null.

Пример: Проверка авторизации

export async function POST(request) {
  const auth = request.headers.get('authorization');
  if (!auth || auth !== 'Bearer supersecrettoken') {
    return new Response('Unauthorized', { status: 401 });
  }
  // ...логика для авторизованных пользователей
}

3. request.json(): получение данных из тела запроса

Самый частый кейс для POST/PUT-запросов — обработка данных, отправленных с клиента. В Next.js Route Handlers для этого используется метод request.json().

export async function POST(request) {
  const data = await request.json();
  // Теперь data — это объект, который отправил клиент
  // Например: { name: "Alice", age: 30 }
}
  • request.json() возвращает промис, поэтому обязательно используйте await.
  • Если тело запроса не является корректным JSON — будет ошибка (try/catch вам в помощь!).
  • Вызывать методы чтения тела (json(), text(), и т.д.) можно только ОДИН раз за обработку, иначе получите ошибку "body already used".

Типичная структура POST-запроса


// Фронтенд отправляет:
fetch('/api/route', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Alice', age: 30 })
});

// Бэкенд принимает:
export async function POST(request) {
  const body = await request.json();
  // body: { name: 'Alice', age: 30 }
}

4. request.text() и request.formData(): альтернативные способы чтения тела

Иногда с клиента отправляют не JSON, а обычный текст или форму (например, через <form> без JS).

  • request.text() — возвращает тело запроса как строку.
  • request.formData() — возвращает объект FormData (для multipart/form-data или application/x-www-form-urlencoded).

export async function POST(request) {
  const text = await request.text();
  // или
  const form = await request.formData();
  const username = form.get('username');
}

5. Объект Response и NextResponse: формируем ответ

В Next.js Route Handlers вы обычно возвращаете либо стандартный объект Response, либо NextResponse (расширение Response с удобствами Next.js).

Простейший ответ:

return new Response('OK');

Ответ с JSON:

return Response.json({ message: 'Hello, world!' });

Ответ с кастомными заголовками и статусом:


return new Response(JSON.stringify({ error: 'Not found' }), {
  status: 404,
  headers: { 'Content-Type': 'application/json' }
});

NextResponse: зачем нужен?

NextResponse — это расширение стандартного Response, добавляющее дополнительные методы (например, для редиректов, управления cookie и т.д.). Для обычных JSON-ответов можно использовать либо Response, либо NextResponse.

6. Практика: пишем мини-API для задач

Давайте добавим в наше учебное приложение API-эндпоинт для создания задач (todo).

Фронтенд:


// Отправляем новую задачу
fetch('/api/todos', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ text: 'Купить хлеб', important: true })
});

Бэкенд (app/api/todos/route.js):


let todos = []; // Имитация "базы данных" в памяти

export async function POST(request) {
  try {
    const data = await request.json();
    // Мини-валидация
    if (!data.text) {
      return Response.json({ error: 'Текст задачи обязателен' }, { status: 400 });
    }
    const todo = {
      id: Date.now(),
      text: data.text,
      important: !!data.important
    };
    todos.push(todo);

    // Отправим обратно созданную задачу
    return Response.json(todo, { status: 201 });
  } catch (err) {
    return Response.json({ error: 'Некорректный JSON' }, { status: 400 });
  }
}
  • Используем await request.json() для чтения тела запроса.
  • Проверяем обязательные поля.
  • Возвращаем созданную задачу с кодом 201 (Created).
  • Если ошибка парсинга — возвращаем ошибку 400.

7. Разбор типичных ошибок при работе с request.json(), headers, method

Ошибка №1: Попытка прочитать тело запроса более одного раза.
Очень частая проблема: если вы дважды вызовете await request.json() (или text(), или formData()), получите ошибку "body already used". Всегда сохраняйте результат в переменную и используйте её повторно.

Ошибка №2: Не проверяется Content-Type.
Если клиент отправил не JSON, а вы вызываете request.json(), будет ошибка. Лучше заранее проверить заголовок:


const contentType = request.headers.get('content-type');
if (!contentType?.includes('application/json')) {
  return new Response('Content-Type must be application/json', { status: 415 });
}

Ошибка №3: Не обрабатываются ошибки парсинга JSON.
Если в теле запроса некорректный JSON, ваш обработчик упадёт. Используйте try/catch!

Ошибка №4: Не проверяется метод запроса.
Если вы не проверяете request.method и пишете одну функцию для всех методов, можно случайно обработать не тот тип запроса. Лучше явно разделять обработчики (или хотя бы проверять метод внутри).

Ошибка №5: Неправильная работа с заголовками.
Заголовки в объекте Headers возвращают строки или null. Не забывайте обрабатывать случай отсутствия заголовка, иначе получите ошибки типа "Cannot read property 'includes' of null".

Ошибка №6: Возврат "сырых" объектов вместо JSON.
Если вы возвращаете обычный объект через new Response(obj), клиент получит строку [object Object]. Используйте Response.json() или JSON.stringify() и правильный Content-Type.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ