Почему возникает ошибка 400 "Missing end time" при создании события в Google Календаре из 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Рассмотрим ситуацию, с которой часто сталкиваются разработчики при интеграции учетных систем с внешними сервисами. Зачастую мы пишем код для отправки POST-запроса через API, авторизуемся с помощью токена, формируем корректный JSON, но в ответ получаем странные ошибки от сервера. В данном случае при попытке создать событие в Google Calendar платформа получает отказ с кодом состояния 400 и сообщением: "Missing end time."

Давайте подробно проанализируем код разработчика, выясним причину такого поведения API Google и разберем по шагам, как правильно формировать HTTP-запросы и сериализовать данные в 1С для безошибочной синхронизации.

Анализ исходного кода и сути проблемы

Посмотрим на пример кода, который использовался для отправки запроса к API Google. В нем формируются HTTP-заголовки, устанавливается тело запроса из строки JSON и вызывается POST-метод.


АдресЗапроса = "https://www.googleapis.com/calendar/v3/calendars/{ИдКалендаря}/events";
ИдКалендаря = Константы.ДБ_ИдентификаторКалендаря.Получить();
АдресЗапроса = СтрЗаменить(АдресЗапроса, "{ИдКалендаря}", ИдКалендаря);
СтруктураURI = СтруктураURI(АдресЗапроса);

HTTPСоединение = Новый HTTPСоединение(СтруктураURI.Хост, 443 , , , , 15, Новый ЗащищенноеСоединениеOpenSSL);

Headers = Новый Соответствие;
Headers.Вставить("Host", СтруктураURI.Хост);
Headers.Вставить("Content-Type", "application/json");
Headers.Вставить("Authorization", "Bearer " + Token);
Headers.Вставить("Content-length", "0");
Headers.Вставить("Accept", "application/json");

HTTPЗапрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере, Headers);
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод("POST", HTTPЗапрос);

Сформированная строка СтрокаЗапроса содержит абсолютно валидный JSON с заполненными полями start и end, которые включают временные метки dateTime. Однако ответ сервера гласит, что время окончания отсутствует.

Выясним причину. Взгляните на строку формирования заголовков: Headers.Вставить("Content-length", "0");. Именно она является виновником сбоя. Заголовок Content-Length сообщает принимающему серверу, сколько байт данных содержится в теле запроса. Указывая здесь 0, мы буквально говорим серверу Google: "Я отправляю тебе POST-запрос, но его тело абсолютно пустое, не читай его".

В результате HTTP-сервер Google игнорирует всю нашу тщательно сформированную строку JSON. Поскольку тело пустое, валидатор Google Calendar API начинает проверять запрос и ожидаемо не находит обязательных полей для создания события. Первым обязательным полем, отсутствие которого вызывает критическую ошибку валидации, является время окончания. Поэтому мы и видим сообщение "Missing end time".

Решение: Платформа 1С при использовании метода УстановитьТелоИзСтроки самостоятельно вычисляет размер строки в байтах и автоматически подставляет правильный заголовок Content-Length. Переопределять его вручную при отправке данных категорически не нужно. Достаточно просто удалить эту строку из формирования заголовков, и событие успешно создастся.

Оптимизация работы с API Google Calendar из 1С

Рассмотрим подробнее, как можно улучшить код синхронизации, опираясь на лучшие практики работы с API и возможности самой платформы 1С. В исходном примере автор самостоятельно преобразует даты через функцию ПреобразоватьДатуВФорматКалендаря, но мы можем сделать это изящнее.

1. Автоматическая сериализация дат

Платформа 1С отлично умеет преобразовывать типы дат в стандарты ISO 8601 "из коробки". Если мы используем объект НастройкиСериализацииJSON, нам не нужно писать отдельные функции для форматирования.


СтруктураСобытия = Новый Структура;
СтруктураСобытия.Вставить("summary", "Заголовок события");
СтруктураСобытия.Вставить("description", "Учимся выгружать события");

// Формируем структуры для начала и конца без строковых преобразований дат
Старт = Новый Структура("timeZone, dateTime", "Europe/Moscow", ВыборкаДетальныеЗаписи.ДатаНачала);
Конец = Новый Структура("timeZone, dateTime", "Europe/Moscow", ВыборкаДетальныеЗаписи.ДатаОкончания);

СтруктураСобытия.Вставить("start", Старт);
СтруктураСобытия.Вставить("end", Конец);

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();

НастройкиСериализации = Новый НастройкиСериализацииJSON;
// Заставляем 1С саму привести все значения типа Дата к формату ISO
НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.ЛокальнаяДатаСоСмещением;

ЗаписатьJSON(ЗаписьJSON, СтруктураСобытия, НастройкиСериализации);
СтрокаJSON = ЗаписьJSON.Закрыть();

2. События на весь день (All-day events)

Разберем ситуацию, когда вам нужно создать событие, которое длится весь день (например, отпуск сотрудника или государственный праздник). Для таких событий структура JSON в Google Calendar должна отличаться.

Вместо свойства dateTime и передачи часовых поясов, необходимо использовать свойство date и передавать строку строго в формате "ГГГГ-ММ-ДД". Передача времени для таких событий вызовет ошибку API.


// Пример структуры для события на весь день
СтартВесьДень = Новый Структура("date", Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd"));
КонецВесьДень = Новый Структура("date", Формат(ТекущаяДата() + 86400, "ДФ=yyyy-MM-dd"));

СтруктураСобытия.Вставить("start", СтартВесьДень);
СтруктураСобытия.Вставить("end", КонецВесьДень);

3. Надежное защищенное соединение (SSL)

В исходном коде объект HTTPСоединение инициализируется параметром Новый ЗащищенноеСоединениеOpenSSL. В некоторых релизах платформы или при особых настройках сервера 1С пустой конструктор этого объекта может привести к разрыву соединения из-за невозможности проверить цепочку сертификатов серверов Google.

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


ssl = Новый ЗащищенноеСоединениеOpenSSL( , Новый СертификатыУдостоверяющихЦентровОС());
HTTPСоединение = Новый HTTPСоединение(СтруктураURI.Хост, 443 , , , , 15, ssl);

4. Управление токенами авторизации (OAuth 2.0)

Проанализируем работу с заголовком Authorization: Bearer. Access-токены, которые выдает Google, обладают коротким временем жизни (обычно 3600 секунд, то есть 1 час). Если попытаться отправить запрос со старым токеном, сервер вернет HTTP статус 401 (Unauthorized).

В полноценной интеграции необходимо реализовать логику перехвата этой ошибки. Посмотрим на алгоритм действий:

  1. Выполняем запрос на создание события с текущим Access-токеном.
  2. Проверяем свойство HTTPОтвет.КодСостояния.
  3. Если код равен 401, значит время токена истекло. В этом случае мы обращаемся к серверу авторизации Google, отправляем наш бессрочный refresh_token и получаем свежий access_token.
  4. Сохраняем новый токен в базу (например, в константы или регистр сведений) и повторяем попытку отправки запроса на создание события.

5. Кодировка UTF-8

При передаче текстов с кириллицей (в полях summary и description) важно использовать правильную кодировку, чтобы в календаре не появились "кракозябры". Использование метода HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать) является абсолютно верным и надежным подходом. Маркер BOM (Byte Order Mark) в начале файла или строки часто ломает REST API сторонних сервисов, поэтому отключать его нужно обязательно.

Применяя данные советы на практике, вы сможете создать стабильный и надежный механизм синхронизации данных из 1С:Предприятие в Google Календарь, избегая типичных ошибок с формированием HTTP-запросов и обработкой структур данных.

← На главную