Как обрабатывать ошибки в HTTP-запросах?react-89

1. Типы ошибок в HTTP-запросах

1.1. Ошибки сети

  • Проблемы соединения
  • Отсутствие интернета
  • CORS-ошибки

1.2. HTTP-ошибки

  • 4xx (клиентские ошибки): 400, 401, 403, 404
  • 5xx (серверные ошибки): 500, 502, 503

1.3. Ошибки парсинга

  • Невалидный JSON
  • Несоответствие структуры данных

2. Обработка ошибок с Fetch API

Базовый пример с обработкой всех типов ошибок:

async function fetchWithErrorHandling(url) {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      // Обработка HTTP-ошибок (4xx, 5xx)
      const errorData = await response.json().catch(() => null);
      throw {
        status: response.status,
        message: errorData?.message || 'HTTP request failed',
        data: errorData
      };
    }

    return await response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Request was aborted');
    } else if (error instanceof TypeError) {
      console.error('Network error:', error.message);
    } else {
      console.error('Request failed:', error);
    }
    throw error; // Пробрасываем ошибку дальше
  }
}

3. Обработка ошибок с Axios

Axios предоставляет более структурированную обработку ошибок:

try {
  const response = await axios.get('/api/data');
  // Обработка успешного ответа
} catch (error) {
  if (axios.isCancel(error)) {
    console.log('Request canceled:', error.message);
  } else if (error.response) {
    // Сервер ответил с кодом ошибки (4xx, 5xx)
    console.error('Server error:', error.response.status);
    console.error('Error data:', error.response.data);
  } else if (error.request) {
    // Запрос был сделан, но ответ не получен
    console.error('No response received:', error.request);
  } else {
    // Ошибка при настройке запроса
    console.error('Request setup error:', error.message);
  }
}

4. Глобальная обработка ошибок

4.1. Интерсепторы в Axios

// Добавляем интерсептор для обработки ошибок
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      // Перенаправляем на страницу входа
      window.location = '/login';
    } else if (error.response?.status === 403) {
      // Показываем уведомление о недостатке прав
      showNotification('У вас нет доступа к этому ресурсу');
    }

    return Promise.reject(error);
  }
);

4.2. Обертка для Fetch API

async function safeFetch(url, options) {
  try {
    const response = await fetch(url, options);
    if (!response.ok) throw response;
    return await response.json();
  } catch (error) {
    if (error.status === 401) handleUnauthorized();
    if (error.status === 404) handleNotFound();
    throw error;
  }
}

5. Интеграция с React-компонентами

5.1. Использование хука для обработки ошибок

function useApi(endpoint) {
  const [state, setState] = useState({
    data: null,
    loading: true,
    error: null
  });

  useEffect(() => {
    const controller = new AbortController();

    const fetchData = async () => {
      try {
        const response = await fetch(endpoint, {
          signal: controller.signal
        });

        if (!response.ok) throw await response.json();

        setState({
          data: await response.json(),
          loading: false,
          error: null
        });
      } catch (error) {
        if (error.name !== 'AbortError') {
          setState({
            data: null,
            loading: false,
            error: error.message || 'Request failed'
          });
        }
      }
    };

    fetchData();

    return () => controller.abort();
  }, [endpoint]);

  return state;
}

5.2. Отображение ошибок в UI

function UserProfile() {
  const { data, loading, error } = useApi('/api/user');

  if (loading) return <Spinner />;
  if (error) return <ErrorDisplay message={error} />;

  return (
    <div>
      <h1>{data.name}</h1>
      {/* ... */}
    </div>
  );
}

6. Логирование ошибок

6.1. Отправка ошибок на сервер

function logError(error) {
  fetch('/api/error-log', {
    method: 'POST',
    body: JSON.stringify({
      error: error.toString(),
      stack: error.stack,
      timestamp: new Date().toISOString()
    })
  });
}

6.2. Интеграция с Sentry/Rollbar

import * as Sentry from '@sentry/react';

try {
  // API запрос
} catch (error) {
  Sentry.captureException(error);
  throw error;
}

7. Продвинутые стратегии

7.1. Retry механизм

async function fetchWithRetry(url, retries = 3) {
  try {
    return await fetch(url);
  } catch (error) {
    if (retries <= 0) throw error;
    await new Promise(res => setTimeout(res, 1000));
    return fetchWithRetry(url, retries - 1);
  }
}

7.2. Circuit Breaker

class ApiCircuitBreaker {
  constructor(maxFailures = 3, cooldown = 30000) {
    this.failures = 0;
    this.lastFailure = 0;
    this.maxFailures = maxFailures;
    this.cooldown = cooldown;
  }

  async call(fn) {
    if (this.failures >= this.maxFailures) {
      if (Date.now() - this.lastFailure < this.cooldown) {
        throw new Error('Service unavailable (circuit breaker)');
      }
      this.failures = 0;
    }

    try {
      const result = await fn();
      this.failures = 0;
      return result;
    } catch (error) {
      this.failures++;
      this.lastFailure = Date.now();
      throw error;
    }
  }
}

Резюмируем

эффективная обработка ошибок в HTTP-запросах требует:

  1. Различения типов ошибок (сеть, сервер, клиент)
  2. Глобального подхода через интерсепторы/обертки
  3. Интеграции с UI для отображения пользователю
  4. Логирования для последующего анализа
  5. Продвинутых стратегий (retry, circuit breaker) для устойчивости приложения