Рассмотрим ситуацию, с которой часто сталкиваются разработчики при интеграции учетных систем с внешними сервисами. Зачастую мы пишем код для отправки 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 и возможности самой платформы 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).
В полноценной интеграции необходимо реализовать логику перехвата этой ошибки. Посмотрим на алгоритм действий:
HTTPОтвет.КодСостояния.refresh_token и получаем свежий access_token.5. Кодировка UTF-8
При передаче текстов с кириллицей (в полях summary и description) важно использовать правильную кодировку, чтобы в календаре не появились "кракозябры". Использование метода HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать) является абсолютно верным и надежным подходом. Маркер BOM (Byte Order Mark) в начале файла или строки часто ломает REST API сторонних сервисов, поэтому отключать его нужно обязательно.
Применяя данные советы на практике, вы сможете создать стабильный и надежный механизм синхронизации данных из 1С:Предприятие в Google Календарь, избегая типичных ошибок с формированием HTTP-запросов и обработкой структур данных.