Обработка ошибок маршрутизации

Маршрутизация в Google Maps JavaScript API строится вокруг сервиса Google Maps JavaScript API DirectionsService, который выполняет запросы к серверу маршрутов, и DirectionsRenderer, отвечающего за визуализацию результата. Несмотря на стабильность платформы Google, запросы маршрутов подвержены множеству факторов отказа: от некорректных входных данных до ограничений квот и сетевых сбоев.

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


Основные причины ошибок маршрутизации

Ошибки при построении маршрута возникают на нескольких уровнях:

1. Ошибки входных данных

  • некорректный формат координат
  • пустые или неполные адреса
  • невозможность геокодирования точки отправления или назначения

2. Ограничения сервиса

  • превышение квоты запросов
  • временная недоступность сервиса маршрутов
  • блокировка API-ключа

3. Логические ограничения маршрута

  • отсутствие автомобильного, пешеходного или другого маршрута между точками
  • пересечение закрытых зон
  • невозможность построить маршрут для заданного режима travelMode

4. Сетевые ошибки

  • таймаут запроса
  • нестабильное соединение
  • прерывание HTTP-запроса

Статусы ошибок Directions API

Результат запроса DirectionsService содержит поле status, которое определяет итог выполнения операции.

Наиболее важные значения:

OK Маршрут успешно построен, данные доступны в результате.

NOT_FOUND Одна или обе точки не могут быть геокодированы.

ZERO_RESULTS Маршрут между точками отсутствует.

MAX_WAYPOINTS_EXCEEDED Превышено допустимое количество промежуточных точек.

MAX_ROUTE_LENGTH_EXCEEDED Маршрут слишком длинный для расчёта.

INVALID_REQUEST Ошибка в структуре запроса (например, отсутствуют обязательные параметры).

OVER_QUERY_LIMIT Превышен лимит запросов API-ключа.

REQUEST_DENIED Запрос отклонён (ключ недействителен или отсутствуют права доступа).

UNKNOWN_ERROR Временная ошибка сервера, часто повторяемая при последующих попытках.


Базовая проверка ошибок при построении маршрута

Классическая схема обработки ошибок строится на анализе status в callback функции DirectionsService:

const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();

directionsRenderer.setMap(map);

const request = {
  origin: "Moscow",
  destination: "Saint Petersburg",
  travelMode: google.maps.TravelMode.DRIVING
};

directionsService.route(request, (result, status) => {
  if (status === "OK") {
    directionsRenderer.setDirections(result);
    return;
  }

  if (status === "ZERO_RESULTS") {
    console.error("Маршрут не найден между точками");
    return;
  }

  if (status === "NOT_FOUND") {
    console.error("Одна из точек не может быть определена");
    return;
  }

  console.error("Ошибка маршрутизации:", status);
});

Данный подход обеспечивает минимально необходимую обработку, но не покрывает повторные попытки и сетевые сбои.


Обработка ошибок в Promise-версии маршрутизации

В современных версиях Google Maps JavaScript API используется асинхронный API через Promise:

const directionsService = new google.maps.DirectionsService();

async function calculateRoute() {
  const request = {
    origin: "Berlin",
    destination: "Munich",
    travelMode: google.maps.TravelMode.DRIVING
  };

  try {
    const result = await directionsService.route(request);

    if (result.status !== "OK") {
      throw new Error(result.status);
    }

    directionsRenderer.setDirections(result);
  } catch (error) {
    console.error("Ошибка при построении маршрута:", error.message);
  }
}

Promise-форма упрощает композицию логики, особенно при цепочках fallback-стратегий.


Обработка ZERO_RESULTS и альтернативные маршруты

Ситуация ZERO_RESULTS означает отсутствие доступного маршрута при заданных параметрах, но не всегда означает полную невозможность навигации.

Практическая стратегия обработки:

  • переключение travelMode
  • упрощение маршрута (убрать waypoints)
  • изменение точки назначения на ближайшую доступную
async function safeRoute(request) {
  let response = await directionsService.route(request);

  if (response.status === "ZERO_RESULTS") {
    const fallbackRequest = {
      ...request,
      travelMode: google.maps.TravelMode.WALKING,
      waypoints: []
    };

    response = await directionsService.route(fallbackRequest);
  }

  return response;
}

Обработка OVER_QUERY_LIMIT и стратегия повторов

OVER_QUERY_LIMIT возникает при превышении квоты запросов. Простое повторение запроса без задержки приводит к каскадным ошибкам.

Используется стратегия экспоненциальной задержки:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function routeWithRetry(request, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const result = await directionsService.route(request);

    if (result.status === "OK") {
      return result;
    }

    if (result.status !== "OVER_QUERY_LIMIT") {
      throw new Error(result.status);
    }

    await sleep(Math.pow(2, i) * 500);
  }

  throw new Error("Превышен лимит запросов");
}

Такая схема снижает нагрузку и увеличивает вероятность успешного ответа при кратковременных перегрузках.


Обработка REQUEST_DENIED и INVALID_REQUEST

Эти ошибки относятся к системным и не подлежат повтору запроса без изменения параметров.

REQUEST_DENIED

  • отсутствует API key
  • ключ ограничен по HTTP referrer
  • не включён Directions API в проекте

INVALID_REQUEST

  • отсутствует origin или destination
  • некорректный travelMode
  • неправильная структура объекта запроса

Пример строгой валидации перед запросом:

function validateRequest(req) {
  if (!req.origin || !req.destination) {
    throw new Error("origin и destination обязательны");
  }

  if (!req.travelMode) {
    throw new Error("travelMode не указан");
  }
}

Обработка UNKNOWN_ERROR и нестабильных ответов

UNKNOWN_ERROR считается временной серверной ошибкой. Обычно она требует повторного запроса с задержкой.

Рекомендуемая стратегия:

  • до 3–5 повторов
  • увеличение интервала между попытками
  • логирование всех попыток

Централизованная система обработки ошибок маршрутов

В сложных приложениях целесообразно выносить обработку статусов в отдельный слой:

function handleDirectionsStatus(status) {
  switch (status) {
    case "OK":
      return { ok: true };

    case "ZERO_RESULTS":
      return { ok: false, message: "Маршрут не найден" };

    case "NOT_FOUND":
      return { ok: false, message: "Точка не найдена" };

    case "OVER_QUERY_LIMIT":
      return { ok: false, retry: true };

    case "REQUEST_DENIED":
      return { ok: false, fatal: true, message: "Доступ запрещён" };

    case "INVALID_REQUEST":
      return { ok: false, fatal: true, message: "Некорректный запрос" };

    default:
      return { ok: false, retry: true, message: status };
  }
}

Такой слой позволяет унифицировать реакцию приложения на любые сценарии отказа.


Логирование и диагностика ошибок маршрутизации

Для стабильной эксплуатации системы маршрутизации важно фиксировать:

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

Пример структуры логирования:

function logRouteError(request, status) {
  console.log({
    origin: request.origin,
    destination: request.destination,
    travelMode: request.travelMode,
    status: status,
    timestamp: Date.now()
  });
}

Такая информация критична для анализа деградации сервиса и выявления проблем с квотами или геоданными.


Комбинированные стратегии устойчивой маршрутизации

Практически используемые приложения комбинируют несколько подходов:

  • предварительная валидация запросов
  • fallback на альтернативные travelMode
  • повторные попытки при временных ошибках
  • централизованная обработка статусов
  • логирование всех отказов

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