Восстановление после ошибок

Universal Router предоставляет гибкий механизм для маршрутизации запросов в приложениях на JavaScript, включая возможность обработки ошибок. Ошибки могут возникать на разных уровнях: при разрешении маршрута, при выполнении асинхронных действий или при генерации контента. Для эффективного восстановления после ошибок необходимо понимать архитектуру маршрутизатора и способы интеграции обработчиков ошибок.

Принцип работы с ошибками

Каждый маршрут в Universal Router описывается объектом с ключами path и action. Функция action может быть асинхронной и возвращать промис. Если в процессе выполнения происходит ошибка, промис отклоняется, что позволяет централизованно или локально обрабатывать исключения.

const router = new UniversalRouter([
  {
    path: '/user/:id',
    action: async ({ params }) => {
      if (!params.id) {
        throw new Error('ID пользователя не указан');
      }
      return fetchUserData(params.id);
    }
  }
]);

В приведенном примере при отсутствии id генерируется ошибка. Universal Router позволяет перехватывать такие ошибки на уровне маршрута или глобально.

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

Для централизованной обработки ошибок используется метод resolve с блоком catch. Это позволяет перехватывать все ошибки, возникающие в маршрутах, и возвращать стандартный ответ или перенаправлять на страницу ошибки.

router.resolve({ pathname: '/user/' })
  .then(result => {
    console.log('Результат маршрута:', result);
  })
  .catch(error => {
    console.error('Произошла ошибка при маршрутизации:', error);
    return renderErrorPage(error);
  });

Ключевым моментом является то, что даже при асинхронных ошибках промис автоматически перейдет в состояние rejected, и обработка в catch позволит корректно восстановить состояние приложения.

Локальная обработка ошибок в маршрутах

Иногда требуется обработка ошибок непосредственно в маршруте, чтобы предоставить специфическую логику восстановления или альтернативный маршрут. Для этого в action можно использовать блоки try/catch.

const router = new UniversalRouter([
  {
    path: '/profile/:id',
    action: async ({ params }) => {
      try {
        const profile = await fetchProfile(params.id);
        return profile;
      } catch (err) {
        console.warn('Ошибка загрузки профиля:', err);
        return redirectTo('/error-profile');
      }
    }
  }
]);

В данном примере ошибка загрузки профиля не прерывает работу всего маршрутизатора, а направляет пользователя на альтернативную страницу.

Перехват ошибок через наследование маршрутов

Universal Router поддерживает вложенные маршруты, что позволяет создавать цепочки маршрутизации с собственными обработчиками ошибок. Родительский маршрут может перехватывать ошибки дочерних маршрутов и выполнять централизованное восстановление.

const router = new UniversalRouter([
  {
    path: '/dashboard',
    action: async ({ next }) => {
      try {
        return await next();
      } catch (err) {
        console.error('Ошибка на панели:', err);
        return renderDashboardFallback();
      }
    },
    children: [
      { path: 'stats', action: () => fetchStats() },
      { path: 'settings', action: () => fetchSettings() }
    ]
  }
]);

Использование next() позволяет обрабатывать ошибки всех дочерних маршрутов в одном месте, упрощая управление восстановлением состояния.

Ошибки 404 и маршруты по умолчанию

Часто необходимо обрабатывать ситуации, когда путь не совпадает ни с одним маршрутом. Для этого можно использовать маршрут с path: '(.*)', который будет перехватывать все неопределенные пути.

const router = new UniversalRouter([
  // другие маршруты
  {
    path: '(.*)',
    action: () => renderNotFoundPage()
  }
]);

Такой подход позволяет создать универсальный механизм восстановления для любых некорректных запросов.

Асинхронные операции и обработка ошибок

При работе с асинхронными действиями важно помнить, что промисы могут быть отклонены в любой момент. Рекомендуется оборачивать критические вызовы в блоки try/catch или использовать Promise.catch, чтобы предотвратить падение приложения.

async function fetchUserData(id) {
  try {
    const response = await fetch(`/api/user/${id}`);
    if (!response.ok) throw new Error('Сервер вернул ошибку');
    return response.json();
  } catch (err) {
    console.error('Ошибка при запросе данных пользователя:', err);
    throw err;
  }
}

Логирование и уведомление об ошибках

Для полноценного восстановления после ошибок важно логировать все исключения и при необходимости уведомлять пользователя или службу мониторинга. Это позволяет выявлять системные проблемы и улучшать стабильность приложения.

router.resolve({ pathname: '/profile/42' })
  .catch(error => {
    logError(error);
    notifyUser('Произошла ошибка при загрузке страницы');
    return renderErrorPage(error);
  });

Итоговые рекомендации по восстановлению после ошибок

  • Всегда обрабатывать ошибки асинхронных действий внутри маршрутов или через глобальный catch.
  • Использовать вложенные маршруты с next() для централизованного перехвата ошибок.
  • Создавать маршруты по умолчанию для обработки несуществующих путей.
  • Логировать ошибки и предоставлять пользователю понятное сообщение или альтернативный контент.
  • Разграничивать критические ошибки (которые требуют перенаправления) и не критические (которые можно игнорировать или показать альтернативные данные).

Эти практики обеспечивают стабильную работу Universal Router и позволяют корректно восстанавливаться после любых ошибок, возникающих на этапе маршрутизации и асинхронной обработки данных.