Что такое коды ответа и чем они отличаются друг от друга?

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#HTTP #API #Web

Краткий ответ

HTTP коды ответа — это трёхзначные числа, которые сервер отправляет клиенту для указания результата обработки запроса:

  1. 1xx — Информационные — запрос получен, обработка продолжается 📡
  2. 2xx — Успешные — запрос успешно обработан ✅
  3. 3xx — Перенаправления — требуются дополнительные действия 🔄
  4. 4xx — Ошибки клиента — ошибка в запросе клиента ❌
  5. 5xx — Ошибки сервера — сервер не смог обработать запрос 💥
// Примеры популярных кодов
200 // OK — всё хорошо
404 // Not Found — страница не найдена
500 // Internal Server Error — ошибка сервера
401 // Unauthorized — требуется авторизация

Полный ответ

HTTP коды ответа — это как светофор в мире веб-разработки. Они говорят нам, что происходит с нашим запросом! 🚦

Классификация кодов ответа

HTTP коды состоят из трёх цифр, где первая цифра определяет класс ответа:

// Структура HTTP кода
[Класс][Подкласс][Конкретный код]
  1-5     0-9        0-9
 
// Примеры
200 // Класс 2 (успех), код 00 (OK)
404 // Класс 4 (ошибка клиента), код 04 (Not Found)

1xx — Информационные коды (100-199)

Эти коды говорят: “Я получил ваш запрос, работаю над ним!” 🔄

// Основные информационные коды
100 // Continue — продолжайте отправку
101 // Switching Protocols — переключение протоколов
102 // Processing — запрос обрабатывается
103 // Early Hints — ранние подсказки
 
// Пример использования
fetch('/api/upload', {
  method: 'POST',
  body: largeFile
}).then(response => {
  // Может получить 100 Continue перед основным ответом
  console.log(response.status); // 200 после завершения
});

2xx — Успешные коды (200-299)

Самые приятные коды — всё прошло отлично! 🎉

// Популярные коды успеха
200 // OK — стандартный успех
201 // Created — ресурс создан
202 // Accepted — запрос принят к обработке
204 // No Content — успех, но нет содержимого
206 // Partial Content — частичное содержимое
 
// Примеры использования
// GET запрос
fetch('/api/users')
  .then(response => {
    console.log(response.status); // 200 OK
    return response.json();
  });
 
// POST запрос (создание)
fetch('/api/users', {
  method: 'POST',
  body: JSON.stringify(userData)
}).then(response => {
  console.log(response.status); // 201 Created
});
 
// DELETE запрос
fetch('/api/users/123', {
  method: 'DELETE'
}).then(response => {
  console.log(response.status); // 204 No Content
});

3xx — Перенаправления (300-399)

Коды говорят: “То, что вы ищете, находится в другом месте!” 🗺️

// Коды перенаправления
300 // Multiple Choices — несколько вариантов
301 // Moved Permanently — постоянное перенаправление
302 // Found — временное перенаправление
304 // Not Modified — не изменялось
307 // Temporary Redirect — временное перенаправление
308 // Permanent Redirect — постоянное перенаправление
 
// Обработка перенаправлений
fetch('/old-page', {
  redirect: 'follow' // автоматически следовать перенаправлениям
}).then(response => {
  if (response.redirected) {
    console.log('Перенаправлен на:', response.url);
  }
});
 
// Кэширование с 304
fetch('/api/data', {
  headers: {
    'If-None-Match': etag // проверка кэша
  }
}).then(response => {
  if (response.status === 304) {
    console.log('Данные не изменились, используем кэш');
  }
});

4xx — Ошибки клиента (400-499)

“Вы что-то сделали не так!” — говорят эти коды 🤦‍♂️

// Популярные ошибки клиента
400 // Bad Request — неправильный запрос
401 // Unauthorized — требуется авторизация
403 // Forbidden — доступ запрещён
404 // Not Found — не найдено
405 // Method Not Allowed — метод не разрешён
409 // Conflict — конфликт данных
422 // Unprocessable Entity — невалидные данные
429 // Too Many Requests — слишком много запросов
 
// Обработка ошибок клиента
async function handleRequest() {
  try {
    const response = await fetch('/api/protected', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
 
    switch (response.status) {
      case 400:
        throw new Error('Неправильный запрос');
      case 401:
        // Перенаправляем на страницу входа
        window.location.href = '/login';
        break;
      case 403:
        throw new Error('Доступ запрещён');
      case 404:
        throw new Error('Ресурс не найден');
      case 422:
        const errors = await response.json();
        console.log('Ошибки валидации:', errors);
        break;
      case 429:
        throw new Error('Слишком много запросов, попробуйте позже');
    }
  } catch (error) {
    console.error('Ошибка:', error.message);
  }
}

5xx — Ошибки сервера (500-599)

“Это не ваша вина, у нас проблемы!” 🔥

// Ошибки сервера
500 // Internal Server Error — внутренняя ошибка
501 // Not Implemented — не реализовано
502 // Bad Gateway — плохой шлюз
503 // Service Unavailable — сервис недоступен
504 // Gateway Timeout — таймаут шлюза
505 // HTTP Version Not Supported — версия HTTP не поддерживается
 
// Обработка серверных ошибок
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      
      if (response.status >= 500) {
        throw new Error(`Серверная ошибка: ${response.status}`);
      }
      
      return response;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      // Экспоненциальная задержка
      await new Promise(resolve => 
        setTimeout(resolve, Math.pow(2, i) * 1000)
      );
    }
  }
}

Таблица популярных кодов

КодНазваниеЗначениеКогда использовать
200OKУспешный запросGET, PUT запросы
201CreatedРесурс созданPOST запросы
204No ContentУспех без содержимогоDELETE запросы
301Moved PermanentlyПостоянное перенаправлениеИзменение URL
304Not ModifiedНе изменялосьКэширование
400Bad RequestНеправильный запросОшибки валидации
401UnauthorizedНе авторизованНужен логин
403ForbiddenДоступ запрещёнНет прав
404Not FoundНе найденоНесуществующий ресурс
500Internal Server ErrorОшибка сервераПроблемы на сервере

Практические примеры обработки

Универсальный обработчик ответов

class ApiClient {
  async request(url, options = {}) {
    try {
      const response = await fetch(url, {
        headers: {
          'Content-Type': 'application/json',
          ...options.headers
        },
        ...options
      });
 
      // Обработка по классам кодов
      if (response.status >= 200 && response.status < 300) {
        return this.handleSuccess(response);
      } else if (response.status >= 300 && response.status < 400) {
        return this.handleRedirect(response);
      } else if (response.status >= 400 && response.status < 500) {
        throw await this.handleClientError(response);
      } else if (response.status >= 500) {
        throw await this.handleServerError(response);
      }
    } catch (error) {
      console.error('Ошибка запроса:', error);
      throw error;
    }
  }
 
  async handleSuccess(response) {
    if (response.status === 204) {
      return null; // No Content
    }
    return response.json();
  }
 
  handleRedirect(response) {
    // Браузер автоматически обрабатывает перенаправления
    return response;
  }
 
  async handleClientError(response) {
    const error = await response.json();
    
    switch (response.status) {
      case 400:
        return new Error(`Неправильный запрос: ${error.message}`);
      case 401:
        this.redirectToLogin();
        return new Error('Требуется авторизация');
      case 403:
        return new Error('Доступ запрещён');
      case 404:
        return new Error('Ресурс не найден');
      case 422:
        return new Error(`Ошибки валидации: ${JSON.stringify(error.errors)}`);
      default:
        return new Error(`Ошибка клиента: ${response.status}`);
    }
  }
 
  async handleServerError(response) {
    return new Error(`Ошибка сервера: ${response.status}`);
  }
 
  redirectToLogin() {
    window.location.href = '/login';
  }
}
 
// Использование
const api = new ApiClient();
 
// GET запрос
const users = await api.request('/api/users');
 
// POST запрос
const newUser = await api.request('/api/users', {
  method: 'POST',
  body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});

React хук для работы с API

import { useState, useCallback } from 'react';
 
function useApi() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
 
  const request = useCallback(async (url, options = {}) => {
    setLoading(true);
    setError(null);
 
    try {
      const response = await fetch(url, options);
      
      if (!response.ok) {
        const errorMessage = await getErrorMessage(response);
        throw new Error(errorMessage);
      }
 
      const data = response.status === 204 ? null : await response.json();
      return data;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  }, []);
 
  return { request, loading, error };
}
 
async function getErrorMessage(response) {
  const statusMessages = {
    400: 'Неправильный запрос',
    401: 'Требуется авторизация',
    403: 'Доступ запрещён',
    404: 'Ресурс не найден',
    422: 'Ошибка валидации данных',
    429: 'Слишком много запросов',
    500: 'Внутренняя ошибка сервера',
    502: 'Плохой шлюз',
    503: 'Сервис недоступен',
    504: 'Таймаут шлюза'
  };
 
  try {
    const errorData = await response.json();
    return errorData.message || statusMessages[response.status] || `Ошибка ${response.status}`;
  } catch {
    return statusMessages[response.status] || `Ошибка ${response.status}`;
  }
}
 
// Использование в компоненте
function UserList() {
  const { request, loading, error } = useApi();
  const [users, setUsers] = useState([]);
 
  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const data = await request('/api/users');
        setUsers(data);
      } catch (err) {
        console.error('Не удалось загрузить пользователей:', err.message);
      }
    };
 
    fetchUsers();
  }, [request]);
 
  if (loading) return <div>Загрузка...</div>;
  if (error) return <div>Ошибка: {error}</div>;
 
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Лучшие практики

1. Правильная обработка ошибок

// ✅ Хорошо: детальная обработка
async function createUser(userData) {
  try {
    const response = await fetch('/api/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(userData)
    });
 
    if (response.status === 201) {
      return await response.json();
    } else if (response.status === 400) {
      const errors = await response.json();
      throw new ValidationError(errors);
    } else if (response.status === 409) {
      throw new ConflictError('Пользователь уже существует');
    } else {
      throw new Error(`Неожиданная ошибка: ${response.status}`);
    }
  } catch (error) {
    console.error('Ошибка создания пользователя:', error);
    throw error;
  }
}
 
// ❌ Плохо: игнорирование кодов
async function createUserBad(userData) {
  const response = await fetch('/api/users', {
    method: 'POST',
    body: JSON.stringify(userData)
  });
  
  return response.json(); // Может быть ошибка!
}

2. Использование правильных кодов на сервере

// Express.js примеры
app.post('/api/users', async (req, res) => {
  try {
    // Валидация
    if (!req.body.email) {
      return res.status(400).json({ 
        error: 'Email обязателен' 
      });
    }
 
    // Проверка существования
    const existingUser = await User.findByEmail(req.body.email);
    if (existingUser) {
      return res.status(409).json({ 
        error: 'Пользователь уже существует' 
      });
    }
 
    // Создание
    const user = await User.create(req.body);
    res.status(201).json(user); // Created
  } catch (error) {
    console.error(error);
    res.status(500).json({ 
      error: 'Внутренняя ошибка сервера' 
    });
  }
});
 
app.delete('/api/users/:id', async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) {
      return res.status(404).json({ 
        error: 'Пользователь не найден' 
      });
    }
 
    await user.delete();
    res.status(204).send(); // No Content
  } catch (error) {
    res.status(500).json({ 
      error: 'Ошибка удаления' 
    });
  }
});

Частые ошибки

Неправильное использование кодов

// ❌ Плохо: неправильные коды
app.post('/api/users', (req, res) => {
  if (!req.body.name) {
    return res.status(500).json({ error: 'Имя обязательно' }); // Должно быть 400!
  }
  
  res.status(200).json({ message: 'Создано' }); // Должно быть 201!
});
 
// ✅ Хорошо: правильные коды
app.post('/api/users', (req, res) => {
  if (!req.body.name) {
    return res.status(400).json({ error: 'Имя обязательно' });
  }
  
  const user = createUser(req.body);
  res.status(201).json(user);
});

Игнорирование кодов ответа

// ❌ Плохо: не проверяем статус
fetch('/api/data')
  .then(response => response.json()) // Может быть ошибка!
  .then(data => console.log(data));
 
// ✅ Хорошо: проверяем статус
fetch('/api/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('Ошибка:', error));

Простые правила

  1. 1xx — информация, продолжайте 📡
  2. 2xx — успех, всё отлично! ✅
  3. 3xx — перенаправление, идите туда 🔄
  4. 4xx — ваша ошибка, исправьте запрос ❌
  5. 5xx — наша ошибка, попробуйте позже 💥
  6. Всегда проверяйте коды ответа в клиенте 🔍
  7. Используйте правильные коды на сервере 🎯
  8. Обрабатывайте ошибки gracefully 🛡️

Понимание HTTP кодов поможет вам создавать надёжные и предсказуемые API! 🚀


Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪