Почему возникает ошибка 405 при изменении объекта через OData в 1С и как её исправить?

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

При работе с внешними системами через стандартный интерфейс OData (HTTP-сервисы 1С) программисты часто сталкиваются с ситуацией, когда запрос на получение данных (GET) отрабатывает успешно, а запрос на изменение (PATCH или PUT) возвращает ошибку 405 Method Not Allowed. Особенно это сбивает с толку, когда в браузере по обоим URL-адресам данные видны корректно. В данной статье мы разберем причины этой ошибки, изучим разницу между сущностями и коллекциями и реализуем правильный алгоритм изменения реквизитов.

Анализ исходной ситуации

Рассмотрим сценарий: нам необходимо изменить артикул в справочнике Номенклатура. У нас есть два варианта формирования URL для запроса:

  1. По уникальному идентификатору (GUID): /wstester/odata/standard.odata/Catalog_Номенклатура(guid'cdcb0e84-ba65-11e7-88e8-742f6881dbd9')
  2. С использованием фильтра по коду: /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, нам необходимо реализовать алгоритм из двух шагов. Разберем его по пунктам:

  1. Выполняем GET-запрос с фильтром по коду, чтобы получить Ref_Key (GUID объекта).
  2. Выполняем PATCH-запрос, подставляя полученный 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 Тогда
                Сообщить("Объект успешно обновлен!");
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Оптимизация: Пакетные запросы ($batch)

Рассмотрим ситуацию, когда нам нужно изменить множество объектов. Делать два запроса (GET + PATCH) для каждой позиции неэффективно с точки зрения сетевой нагрузки. В 1С поддерживается механизм $batch, который позволяет объединить несколько операций в один HTTP-вызов.

Внутри одного $batch запроса можно передать сначала команду поиска, а затем команду изменения. Однако это требует сложного формирования тела запроса в формате multipart/mixed. Если производительность критична, стоит изучить этот механизм в официальной документации OData.

Альтернативный подход: Собственный HTTP-сервис

Проанализируем ситуацию: если ваша задача всегда сводится к «найти по коду и изменить», стандартный OData может быть избыточным. В 1С гораздо эффективнее создать собственный HTTP-сервис.

В модуле собственного сервиса вы сможете написать код на языке 1С, который выполнит всю логику за один цикл:


Функция ИзменитьПоКоду(Запрос)
    // Получаем код и данные из тела запроса
    Код = Запрос.ПараметрыURL.Получить("Code");
    Тело = Запрос.ПолучитьТелоКакСтроку();
    
    // Выполняем поиск средствами платформы
    Ссылка = Справочники.Номенклатура.НайтиПоКоду(Код);
    
    Если Ссылка.Пустая() Тогда
        Возврат Новый HTTPСервисОтвет(404);
    КонецЕсли;
    
    Объект = Ссылка.ПолучитьОбъект();
    // Логика заполнения из JSON...
    Объект.Записать();
    
    Возврат Новый HTTPСервисОтвет(200);
КонецФункции

Этот метод избавляет от необходимости знать GUID на стороне вызывающей системы и работает значительно быстрее.

Важные нюансы кодирования и передачи данных

Работая с HTTPЗапрос, всегда помните о следующих правилах:

Подводя итог, отметим: ошибка 405 при использовании фильтров в OData — это не баг, а строгое следование протоколу. Используйте GUID для точечных изменений или переходите на собственные HTTP-сервисы для реализации сложной бизнес-логики.

← На главную