Ответы API и обработка ошибок

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

Личный кабинет

Ответы API и обработка ошибок

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

В этой статье

Порядок проверки ответа

  1. Убедитесь, что ответ получен полностью. Ошибка соединения или чтения тела отличается от ответа сервера с HTTP-кодом отказа.
  2. Прочитайте HTTP-код и Content-Type. Обрабатывайте JSON, текст и файл по предусмотренному для метода сценарию.
  3. При отказе используйте его пояснение. Для JSON проверяйте также корневой error: true, даже если получен HTTP 200.
  4. При успешном ответе проверьте нужную структуру: карточку, список или подтверждение записи. Затем оцените предметный результат.

Для пакетной операции общий HTTP 200 не означает успех каждого элемента. Метод может вернуть отдельные ошибки, предупреждения и пропуски. Проверяйте предусмотренные им результаты элементов и счётчики.

Три исхода запроса: ответ потерян, операция отклонена, получен результат. Для JSON и файла нужны разные проверки.
Полный ответ позволяет перейти к проверке результата; потеря ответа оставляет исход записи неизвестным.

Как выглядит успешный результат

У API нет одной общей обёртки для всех методов. Не используйте result.res.id или result.ok как универсальную проверку.

РезультатЧто проверить
КарточкаРасположение и ID записи по контракту метода. Например, auth/check возвращает пользователя в корне, а карточка заявки ИЛ находится в res.
СписокМассив элементов и сведения о количестве/странице, если они предусмотрены. Пустой разрешённый список является успешным результатом.
ЗаписьПризнак успеха, ID или возвращённые значения, которые обещает метод. Наличие произвольного объекта JSON этого не заменяет.
Пакетная операцияКакие элементы обработаны, отклонены или пропущены. Частичный результат требует отдельного действия для оставшихся элементов.
Текст или файлОжидаемые содержимое и тип. Например, команда версии обмена 1С возвращает текст 1; к ней нельзя применять схему карточки.

Пустое тело допустимо только там, где его предусматривает метод. При чтении карточки или подтверждении сохранения оно само по себе не означает «записей нет» или «сохранено».

Какие сведения содержит ошибка

Стандартный отказ ядра содержит error: true и title. Пояснение mess и остальные поля зависят от причины и метода:

{
  "error": true,
  "title": "Неверный запрос",
  "mess": "Проверьте обязательное поле",
  "field": "id"
}
  • title сообщает краткую причину, mess дополняет её.
  • field указывает проблемное поле, если метод его передал.
  • code может обозначать предметную причину. Используйте его только по описанию метода; он есть не у каждого отказа.
  • error_info и зашифрованный report могут содержать диагностику. Не показывайте их как обычное сообщение и не публикуйте вместе с персональными данными.

Если тело пустое или его невозможно разобрать, причину могут передать заголовки Error-Title и Error-Mess. Они кодируются как URL: при чтении замените + пробелом и выполните decodeURIComponent. Отсутствие этих заголовков не отменяет HTTP-отказ.

Выводите сообщения сервера как текст. Сырое тело ответа, HTML страницы прокси и диагностические фрагменты не подставляйте в HTML окна ошибки.

HTTP-коды и дальнейшие действия

КодДействие
400Проверьте синтаксис тела, обязательные поля и бизнес-условия по field/code/mess.
401Сессия не принята либо пользователь заблокирован. Прекратите запросы с прежними данными авторизации.
403Прочитайте причину ограничения: параметры входа, ключ, право или запрет доступа. Этот код не всегда означает истечение токена.
404Проверьте путь метода, ID и доступность запрошенной записи или файла.
409При конфликте версии получите свежую запись и разрешите расхождение перед новой отправкой. Повтор прежнего тела не устраняет конфликт.
413Уменьшите размер запроса или части файла согласно лимиту конкретного интерфейса.
419Токен закрыт или истёк. Завершите использование старой сессии.
422Разберите причину метода. Например, поддержанная идемпотентность отклоняет прежний ключ с другим телом.
429Сделайте паузу и уменьшите частоту запросов по правилам интерфейса.
5xxОшибка сервера или связанного сервиса. После запроса записи выясните фактический результат перед повтором.

Код помогает выбрать действие, но не заменяет пояснение метода. Порядок восстановления авторизации описан в статье «Срок действия токена, проверка и завершение сессии».

Пример обработки JSON

Этот обработчик рассчитан на метод, от которого ожидается непустой JSON. Он сохраняет HTTP-код и сведения об отказе, использует резервные заголовки и не выводит сырое тело:

async function readApiJson(response) {
  const header = name => {
    const value = response.headers.get(name) || '';
    if (value === 'null') return '';
    try { return decodeURIComponent(value.replaceAll('+', ' ')); }
    catch { return value; }
  };
  const fail = (kind, message, details = {}) =>
    Object.assign(new Error(message), {
      kind, status: response.status, ...details
    });
  let text;
  try { text = await response.text(); }
  catch (cause) {
    if (cause?.name === 'AbortError') throw cause;
    throw fail('network', 'Ответ не получен полностью', {cause});
  }
  const type = (response.headers.get('content-type') || '')
    .split(';')[0].trim().toLowerCase();
  const isJson = type === 'application/json' || type.endsWith('+json');
  let data = null, broken = false;
  if (isJson && text.trim()) {
    try { data = JSON.parse(text); }
    catch { broken = true; }
  }
  const info = data && typeof data === 'object' ? data : {};
  if (!response.ok || info.error === true) {
    const title = info.title || header('Error-Title');
    const mess = info.mess || header('Error-Mess');
    throw fail('api', [title, mess].filter(Boolean).join(': ')
      || `HTTP ${response.status}`, {
        title, mess, field: info.field, code: info.code
      });
  }
  if (!isJson || !text.trim() || broken) {
    throw fail('format', 'Ожидался непустой JSON-ответ');
  }
  return data;
}

Пример чтения периодов местонахождения экземпляра СИ. Здесь id означает ID экземпляра, а результат содержит массив res и общее количество count:

const url = 'https://lab.example/api/laboratory/measuring/place/info';
const response = await fetch(url, {
  method: 'POST',
  headers: {
    auth: 'YOUR_AUTH_TOKEN', Lang: 'ru', Out: 'json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({id: 900101, page: 1})
}).catch(cause => {
  if (cause?.name === 'AbortError') throw cause;
  throw Object.assign(new Error('Ответ не получен'), {
    kind: 'network', cause
  });
});
const data = await readApiJson(response);
if (!Array.isArray(data?.res)
    || !Number.isInteger(data.count) || data.count < 0) {
  throw new Error('Не получен список периодов местонахождения');
}

Замените условные адрес, токен и ID своими значениями. Последняя проверка относится именно к этому методу; у другого метода будет своя схема. Ошибки с kind: 'api' содержат отказ, format означает неподходящий ответ, network означает отсутствие полного ответа. Отмену запроса по собственной команде обрабатывайте отдельно.

Получение файла

  1. Проверьте HTTP-код. При отказе прочитайте предусмотренные JSON или заголовки ошибки.
  2. Сравните Content-Type с форматом, который должен вернуть метод. Для ожидаемого PDF страница HTML или обычный JSON-объект не подходят.
  3. Получите имя из Content-Disposition или FileName, если сервер их передал, либо задайте его по контракту метода.
  4. Только после проверок получите Blob и передайте файл на сохранение или просмотр.

JSON и HTML могут быть законными форматами экспорта. Поэтому проверка зависит от ожидаемого файла. Имя с расширением .pdf не превращает ответ в PDF, а успешное получение файла ещё не означает его сохранение на устройстве.

О передаче вложений: «Загрузка файлов и вложений».

Потеря ответа и повтор запроса

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

После создания или изменения записи сначала выясните её фактическое состояние. Повторяйте отправку по правилам конкретной операции; используйте идемпотентность только там, где она поддерживается. Подробности: «Повторные запросы и диагностика сбоев».

Связанные материалы