Интеграция 1С с системой управления проектами Redmine — это эффективный способ автоматизировать передачу заявок, отслеживание ошибок и учет рабочего времени. Однако при использовании REST API программисты часто сталкиваются с неочевидными ошибками, такими как 422 Unprocessable Entity. Разберем подробно, почему это происходит и как реализовать надежный механизм создания задач.
Когда Redmine возвращает код 422, это означает, что сервер понял запрос, но его содержимое не соответствует правилам валидации. Чаще всего в теле ответа приходят уточнения: «Тема не может быть пустой», «Проект не может быть пустым» и так далее. Рассмотрим основные причины возникновения этой ситуации:
GET-запросе. При получении данных задачи (GET) Redmine возвращает объекты (например, "project": {"id": 1, "name": "Project Name"}), но при создании задачи (POST) он ожидает плоские идентификаторы ("project_id": 1).application/octet-stream или text/plain вместо application/json, сервер может некорректно интерпретировать тело запроса, посчитав его пустым.BOM (Byte Order Mark). Некоторые веб-серверы, на которых развернут Redmine, не умеют ее обрабатывать, что приводит к порче структуры JSON.Для взаимодействия с API Redmine лучше всего использовать персональный API-ключ. Это избавляет от необходимости передавать логин и пароль в открытом виде и упрощает настройку HTTPСоединение. Ключ передается в специальном заголовке X-Redmine-API-Key.
Рассмотрим, как правильно инициализировать соединение в 1С:
// Параметры сервера
Сервер = "redmine.yourcompany.com";
Порт = 443; // Обычно используется HTTPS
Токен = "ваш_api_ключ_из_настроек_профиля";
// Используем защищенное соединение для HTTPS
Защита = Новый ЗащищенноеСоединениеOpenSSL();
HTTPСоединение = Новый HTTPСоединение(Сервер, Порт, , , , 30, Защита);
Выясним разницу между форматом отображения и форматом создания. Чтобы задача была успешно создана, нам необходимо передать минимальный набор обязательных полей, используя суффикс _id для ссылочных полей. Проанализируем пример корректного формирования JSON-объекта внутри 1С с использованием структуры.
ЗадачаОбъект = Новый Структура;
ДанныеЗадачи = Новый Структура;
ДанныеЗадачи.Вставить("project_id", 94); // ID проекта (число)
ДанныеЗадачи.Вставить("tracker_id", 14); // ID трекера (Ошибка, Поддержка и т.д.)
ДанныеЗадачи.Вставить("status_id", 1); // ID статуса (Новая)
ДанныеЗадачи.Вставить("priority_id", 5); // ID приоритета (Высокий)
ДанныеЗадачи.Вставить("subject", "Тестовая тема из 1С");
ДанныеЗадачи.Вставить("description", "Подробное описание проблемы");
ДанныеЗадачи.Вставить("assigned_to_id", 194); // На кого назначена задача
ЗадачаОбъект.Вставить("issue", ДанныеЗадачи);
Обратите внимание: мы не передаем id самой задачи, так как он присваивается сервером автоматически при создании. Также мы используем project_id вместо вложенного объекта project.
Посмотрим на пример правильной записи JSON в строку. Важно использовать объект ЗаписьJSON, чтобы избежать проблем с экранированием кавычек и спецсимволов в тексте описания.
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(, , , ЭкранированиеСимволовJSON.ВсеВнеBasicLatin);
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
ЗаписатьJSON(ЗаписьJSON, ЗадачаОбъект);
СтрокаЗапроса = ЗаписьJSON.Закрыть();
Теперь соберем все воедино в HTTPЗапрос. Помним о важности заголовка Content-Type.
HTTPЗапрос = Новый HTTPЗапрос("/issues.json");
HTTPЗапрос.Заголовки.Вставить("X-Redmine-API-Key", Токен);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
// Устанавливаем тело запроса в кодировке UTF-8 без BOM
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Попытка
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если Ответ.КодСостояния = 201 Тогда
Сообщить("Задача успешно создана!");
Иначе
Сообщить("Ошибка при создании: " + Ответ.КодСостояния);
Сообщить(Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Исключение
Сообщить("Не удалось соединиться с сервером: " + ОписаниеОшибки());
КонецПопытки;
Проанализируем ситуацию, когда к задаче нужно прикрепить файл из 1С. Это двухэтапный процесс, который часто вызывает путаницу:
POST-запросом на адрес /uploads.json. В теле запроса передаются чистые двоичные данные файла (application/octet-stream). В ответ Redmine вернет token.uploads при формировании JSON для создания задачи.Пример структуры вложения для основного запроса:
Вложение = Новый Структура;
Вложение.Вставить("token", "7973.a062c6e4..."); // Токен из первого запроса
Вложение.Вставить("filename", "logs.txt");
Вложение.Вставить("content_type", "text/plain");
МассивВложений = Новый Массив;
МассивВложений.Добавить(Вложение);
ДанныеЗадачи.Вставить("uploads", МассивВложений);
Если в вашем Redmine настроены обязательные дополнительные поля (Custom Fields), их необходимо передавать в виде массива объектов внутри структуры задачи. Выясним формат:
ДопПоле = Новый Структура;
ДопПоле.Вставить("id", 16); // ID кастомного поля
ДопПоле.Вставить("value", "Значение поля");
МассивПолей = Новый Массив;
МассивПолей.Добавить(ДопПоле);
ДанныеЗадачи.Вставить("custom_fields", МассивПолей);
Резюмируем: Для успешного создания задачи в Redmine из 1С необходимо четко соблюдать структуру JSON (использовать _id), обязательно устанавливать заголовок Content-Type: application/json и исключать BOM из передаваемой строки. Использование объекта ЗаписьJSON и HTTPСоединение с защищенным каналом сделает вашу интеграцию стабильной и безопасной.