Маршрут для 404 ошибки

Universal Router — это гибкая библиотека для маршрутизации в JavaScript, которая позволяет создавать как простые, так и сложные маршруты с поддержкой вложенных структур и динамических сегментов. Один из ключевых аспектов построения маршрутизатора — корректная обработка ситуации, когда запрошенный путь не соответствует ни одному определённому маршруту. Это стандартная ситуация «404 Not Found».


Определение маршрута для 404

В Universal Router обработка «страниц не найдено» реализуется с помощью функции resolve или через маршруты, где ключевой момент — отсутствие совпадений. Обычно для этого создаётся маршрут-заглушка, который находится в конце массива маршрутов. Такой маршрут срабатывает, когда ни один предыдущий маршрут не подошёл.

Пример базового определения маршрута 404:

import UniversalRouter from 'universal-router';

const routes = [
  { path: '/', action: () => 'Главная страница' },
  { path: '/about', action: () => 'О нас' },
  { 
    path: '(.*)', // Любой путь, который не был пойман выше
    action: () => {
      return 'Страница не найдена';
    }
  }
];

const router = new UniversalRouter(routes);

router.resolve('/non-existent').then(console.log); // Страница не найдена

Ключевые моменты:

  • Порядок маршрутов имеет значение. Маршрут для 404 должен идти последним, иначе он перехватит все пути.
  • Использование регулярного выражения (.*) позволяет поймать любой путь, не сопоставленный с предыдущими маршрутами.
  • Метод action возвращает контент или объект, который затем используется приложением для отображения ошибки.

Генерация кастомного ответа

Маршрут 404 можно расширить, чтобы возвращать объект с кодом состояния HTTP, что удобно для серверного рендеринга:

const routes = [
  { path: '/', action: () => ({ status: 200, body: 'Главная' }) },
  { path: '/about', action: () => ({ status: 200, body: 'О нас' }) },
  { 
    path: '(.*)',
    action: () => ({ status: 404, body: 'Страница не найдена' })
  }
];

async function handleRequest(path) {
  const result = await router.resolve(path);
  console.log(result.status); // 404
  console.log(result.body);   // Страница не найдена
}

handleRequest('/invalid');

Такой подход позволяет унифицировать обработку ошибок как на клиенте, так и на сервере, возвращая корректный HTTP-статус и тело ответа.


Вложенные маршруты и 404

В сложных приложениях часто используются вложенные маршруты. В этом случае обработка 404 может быть как глобальной, так и локальной для конкретного сегмента.

Пример с вложенными маршрутами:

const routes = [
  {
    path: '/',
    action: () => 'Главная',
    children: [
      { path: 'dashboard', action: () => 'Панель управления' },
      { 
        path: '(.*)',
        action: () => 'Раздел не найден'
      }
    ]
  },
  {
    path: '(.*)',
    action: () => 'Страница не найдена'
  }
];

Особенности:

  • Вложенный 404 (children: [{ path: '(.*)', ... }]) срабатывает для путей внутри родительского маршрута.
  • Глобальный 404, расположенный в корне маршрутов, ловит все остальные пути, не попавшие в дочерние маршруты.
  • Такой подход позволяет различать ошибки на уровне разделов приложения и глобальные ошибки.

Использование context.next() для кастомного поведения

Universal Router предоставляет возможность вызывать context.next() в action, что позволяет передавать обработку на следующий маршрут, если текущий маршрут не подходит. Это полезно для реализации 404 через цепочку маршрутов:

const routes = [
  { path: '/', action: () => 'Главная' },
  { path: '/about', action: () => 'О нас' },
  {
    path: '/special',
    action: async (context) => {
      if (!context.userHasAccess) {
        return context.next(); // Передаём управление следующему маршруту
      }
      return 'Специальная страница';
    }
  },
  {
    path: '(.*)',
    action: () => 'Страница не найдена'
  }
];

Преимущества:

  • Позволяет интегрировать проверку прав доступа и другие условия перед генерацией ошибки 404.
  • Обеспечивает централизованную обработку всех маршрутов, включая защищённые или динамические сегменты.

Динамические маршруты и fallback

Для динамических маршрутов важно предусматривать fallback на 404, если данные для страницы не найдены:

const routes = [
  { path: '/user/:id', action: async ({ params }) => {
      const user = await getUser(params.id);
      if (!user) return 'Пользователь не найден';
      return `Профиль ${user.name}`;
    }
  },
  { path: '(.*)', action: () => 'Страница не найдена' }
];
  • При отсутствии пользователя возвращается локальный 404.
  • Глобальный 404 срабатывает для путей, не соответствующих /user/:id.

Практические рекомендации

  • Всегда размещать маршрут для 404 в конце массива маршрутов.
  • Для вложенных маршрутов использовать локальные 404, чтобы отделять ошибки разделов от глобальных.
  • В серверных приложениях возвращать объект с status и body для корректного HTTP-ответа.
  • Для сложных условий использовать context.next(), чтобы цепочка маршрутов могла корректно передавать управление.
  • В динамических маршрутах проверять наличие данных и возвращать локальный 404 при их отсутствии.

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