При работе с внешними системами через стандартный интерфейс OData (HTTP-сервисы 1С) программисты часто сталкиваются с ситуацией, когда запрос на получение данных (GET) отрабатывает успешно, а запрос на изменение (PATCH или PUT) возвращает ошибку 405 Method Not Allowed. Особенно это сбивает с толку, когда в браузере по обоим URL-адресам данные видны корректно. В данной статье мы разберем причины этой ошибки, изучим разницу между сущностями и коллекциями и реализуем правильный алгоритм изменения реквизитов.
Рассмотрим сценарий: нам необходимо изменить артикул в справочнике Номенклатура. У нас есть два варианта формирования URL для запроса:
/wstester/odata/standard.odata/Catalog_Номенклатура(guid'cdcb0e84-ba65-11e7-88e8-742f6881dbd9')/wstester/odata/standard.odata/Catalog_Номенклатура?$filter=Code eq '000000010'Проблема заключается в том, что первый вариант успешно выполняет метод ИзменитьАсинх (PATCH), а второй — возвращает ошибку 405. Давайте выясним, почему так происходит.
Проанализируем структуру ответов, которые возвращает сервер. Это ключ к пониманию проблемы. Когда мы используем $filter, сервер возвращает JSON, в котором данные обернуты в массив (квадратные скобки []), даже если найден всего один элемент:
{
"odata.metadata": "...",
"value": [
{
"Ref_Key": "cdcb0e84-ba65-11e7-88e8-742f6881dbd9",
"Code": "000000010",
"Артикул": "5456465454"
}
]
}
В терминах OData это называется Entity Set (Набор сущностей). Спецификация протокола и реализация в 1С запрещают применение методов изменения (PATCH, PUT, DELETE) непосредственно к набору. Фильтр потенциально может вернуть несколько объектов, и система «защищает» данные от массового неочевидного изменения.
Когда же мы обращаемся по guid, сервер возвращает одну конкретную Entity (Сущность) без массива value. Только к такому адресу применим метод PATCH.
Если на стороне клиента нам известен только код товара, а не его GUID, нам необходимо реализовать алгоритм из двух шагов. Разберем его по пунктам:
Ref_Key (GUID объекта).Посмотрим на пример кода, который реализует поиск и последующее изменение:
&НаКлиенте
Асинх Процедура ИзменитьДанныеПоКоду(КодОбъекта, НовыйАртикул)
Сервер = "localhost";
// Шаг 1: Формируем запрос для поиска GUID
Фильтр = "Code eq '" + КодОбъекта + "'";
// Важно закодировать строку фильтра для URL
ФильтрЗакодированный = КодироватьСтроку(Фильтр, СпособКодированияСтроки.КодировкаURL);
АдресПоиска = "/wstester/odata/standard.odata/Catalog_Номенклатура?$filter=" + ФильтрЗакодированный + "&$format=json";
Соединение = Новый HTTPСоединение(Сервер);
ЗапросПоиска = Новый HTTPЗапрос(АдресПоиска);
ОтветПоиска = Ждать Соединение.ПолучитьАсинх(ЗапросПоиска);
Если ОтветПоиска.КодСостояния = 200 Тогда
// Читаем ответ и извлекаем Ref_Key
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ОтветПоиска.ПолучитьТелоКакСтроку());
ДанныеПоиска = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Если ДанныеПоиска.value.Количество() > 0 Тогда
GUID = ДанныеПоиска.value[0].Ref_Key;
// Шаг 2: Выполняем изменение
АдресИзменения = "/wstester/odata/standard.odata/Catalog_Номенклатура(guid'" + GUID + "')";
ЗапросИзменения = Новый HTTPЗапрос(АдресИзменения);
// Формируем тело запроса с новыми данными
СтруктураДанных = Новый Структура("Артикул", НовыйАртикул);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
ТелоЗапроса = ЗаписьJSON.Закрыть();
ЗапросИзменения.УстановитьТелоИзСтроки(ТелоЗапроса);
ОтветИзменения = Ждать Соединение.ИзменитьАсинх(ЗапросИзменения);
Если ОтветИзменения.КодСостояния = 200 Тогда
Сообщить("Объект успешно обновлен!");
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Рассмотрим ситуацию, когда нам нужно изменить множество объектов. Делать два запроса (GET + PATCH) для каждой позиции неэффективно с точки зрения сетевой нагрузки. В 1С поддерживается механизм $batch, который позволяет объединить несколько операций в один HTTP-вызов.
Внутри одного $batch запроса можно передать сначала команду поиска, а затем команду изменения. Однако это требует сложного формирования тела запроса в формате multipart/mixed. Если производительность критична, стоит изучить этот механизм в официальной документации OData.
Проанализируем ситуацию: если ваша задача всегда сводится к «найти по коду и изменить», стандартный OData может быть избыточным. В 1С гораздо эффективнее создать собственный HTTP-сервис.
В модуле собственного сервиса вы сможете написать код на языке 1С, который выполнит всю логику за один цикл:
Функция ИзменитьПоКоду(Запрос)
// Получаем код и данные из тела запроса
Код = Запрос.ПараметрыURL.Получить("Code");
Тело = Запрос.ПолучитьТелоКакСтроку();
// Выполняем поиск средствами платформы
Ссылка = Справочники.Номенклатура.НайтиПоКоду(Код);
Если Ссылка.Пустая() Тогда
Возврат Новый HTTPСервисОтвет(404);
КонецЕсли;
Объект = Ссылка.ПолучитьОбъект();
// Логика заполнения из JSON...
Объект.Записать();
Возврат Новый HTTPСервисОтвет(200);
КонецФункции
Этот метод избавляет от необходимости знать GUID на стороне вызывающей системы и работает значительно быстрее.
Работая с HTTPЗапрос, всегда помните о следующих правилах:
КодироватьСтроку(Строка, СпособКодированияСтроки.КодировкаURL). Просто заменить пробел на %20 вручную недостаточно, так как в кодах могут быть знаки #, & или кириллица.true/false без кавычек.DataVersion. Рекомендуется передавать его обратно в заголовке If-Match при выполнении PATCH. Это реализует оптимистическую блокировку: если кто-то другой изменил объект, пока вы готовили данные, сервер вернет ошибку 412, предотвращая перезапись актуальных данных устаревшими.Подводя итог, отметим: ошибка 405 при использовании фильтров в OData — это не баг, а строгое следование протоколу. Используйте GUID для точечных изменений или переходите на собственные HTTP-сервисы для реализации сложной бизнес-логики.