Почему при отправке заказа поставщику через API ABCP возникает ошибка 500?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← К списку

При работе с внешними API, такими как API ABCP, отправка данных и получение ответов — это стандартная задача. Однако иногда мы сталкиваемся с неожиданными ошибками, которые требуют детального анализа. Одной из таких распространенных проблем является ошибка HTTP 500 Internal Server Error при попытке отправить заказ. Давайте вместе разберем эту ситуацию и выясним, как ее эффективно решить.

Изначально мы имеем следующую попытку отправки запроса:


ТекстЗапроса = "&orderParams[shipmentAddress]=FD765B07-2B9E-11E9-A2C9-005056802F4C&orderParams[paymentMethod]=2&orderParams[shipmentMethod]=1&orderParams[shippingDateLast]=1&orderParams[transportType]=1&positions[0][id]=98989898&positions[0][positionParams][comment]=тест";
ФрагментЗапроса = "userlogin=api@abcp****&userpsw=d07**************9c" + ТекстЗапроса;
HTTPСоединение = Новый HTTPСоединение("abcp****.public.api.abcp.ru",,,,,, Новый ЗащищенноеСоединениеOpenSSL());
HTTPЗапрос = Новый HTTPЗапрос("/cp/orders/online");
HTTPЗапрос.УстановитьТелоИзСтроки(ФрагментЗапроса);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
Попытка
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос, ИмяВыходногоФайла);    
    КодСостояния = HTTPОтвет.КодСостояния;
Исключение
    ТекстСообщения = "Код ошибки"+КодСостояния+"ошибка получения заказов:"+ОписаниеОшибки();
    Сообщить(ТекстСообщения);
    Возврат;
КонецПопытки;

При выполнении этого кода мы получаем ошибку HTTP 500. Давайте проанализируем, что она означает и как мы можем ее исправить.

1. Что означает ошибка HTTP 500 и почему она возникает?

Ошибка HTTP 500 Internal Server Error — это общая ошибка на стороне сервера. Она указывает на то, что сервер столкнулся с непредвиденным условием, которое помешало ему обработать наш запрос. Это означает, что проблема, скорее всего, не в нашем соединении или коде 1С, а на стороне веб-сервера ABCP. Причины такой ошибки могут быть разнообразны:

  1. Ошибки в коде приложения на сервере: например, нехватка памяти, таймаут выполнения скрипта, попытка включить несуществующий файл или деление на ноль.
  2. Проблемы с базой данных: ошибки при чтении или записи данных.
  3. Некорректные права доступа к файлам или папкам на сервере.
  4. Поврежденная конфигурация веб-сервера.

Что мы можем сделать?

Поскольку это серверная ошибка, наиболее точную информацию о ее причине можно получить только из логов сервера ABCP. Мы настоятельно рекомендуем обратиться в службу поддержки ABCP. При обращении обязательно укажите:

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

2. Правильное URL-кодирование параметров для Content-Type: application/x-www-form-urlencoded

Одна из наиболее частых причин ошибки 500 при работе с API заключается в некорректном формировании тела запроса, особенно когда используется тип содержимого application/x-www-form-urlencoded. Давайте рассмотрим подробнее, в чем здесь может быть проблема.

При использовании application/x-www-form-urlencoded все неалфавитно-цифровые символы как в ключах, так и в значениях параметров должны быть процентно-кодированы (URL-encoded). Специальные символы, такие как & (амперсанд), = (знак равенства), ? (вопросительный знак), (пробел), а также [ ] (квадратные скобки) и { } (фигурные скобки), являются зарезервированными или "небезопасными" в URL и должны быть закодированы, если они используются как часть данных, а не как разделители.

В нашем исходном запросе мы видим следующую строку:


ТекстЗапроса = "&orderParams[shipmentAddress]=FD765B07-2B9E-11E9-A2C9-005056802F4C&orderParams[paymentMethod]=2&orderParams[shipmentMethod]=1&orderParams[shippingDateLast]=1&orderParams[transportType]=1&positions[0][id]=98989898&positions[0][positionParams][comment]=тест";

Здесь присутствуют квадратные скобки [ и ] в именах параметров, например, orderParams[shipmentAddress] и positions[0][id]. Хотя некоторые API могут интерпретировать их как синтаксис массива без кодирования, общее правило для application/x-www-form-urlencoded требует их кодирования, если они не являются частью строго определенного синтаксиса API, который не требует их кодирования. Также важно помнить о кодировании логина (userlogin) и пароля (userpsw), если они содержат специальные символы.

Ключевой момент: кодировать нужно каждое отдельное значение!

Мы должны кодировать не всю строку запроса целиком, а каждое отдельное значение, которое вставляем в итоговую строку. Для этого в 1С мы используем функцию КодироватьСтроку с параметром СпособКодированияСтроки.КодировкаURL.

Рассмотрим пример правильного подхода к кодированию:


// Пример кодирования отдельных значений
ЗначениеАдресаОтгрузки = "FD765B07-2B9E-11E9-A2C9-005056802F4C";
ЗначениеКомментария = "тест с пробелами и символами & =";
ЛогинПользователя = "api@abcp****";
ПарольПользователя = "d07**************9c";

// Кодируем каждое значение
КодированноеЗначениеАдресаОтгрузки = КодироватьСтроку(ЗначениеАдресаОтгрузки, СпособКодированияСтроки.КодировкаURL);
КодированноеЗначениеКомментария = КодироватьСтроку(ЗначениеКомментария, СпособКодированияСтроки.КодировкаURL);
КодированныйЛогин = КодироватьСтроку(ЛогинПользователя, СпособКодированияСтроки.КодировкаURL);
КодированныйПароль = КодироватьСтроку(ПарольПользователя, СпособКодированияСтроки.КодировкаURL);

// Формируем строку запроса, где ключи также могут быть закодированы, если они содержат специальные символы
// В данном случае, ABCP API обычно ожидает квадратные скобки без кодирования в именах параметров,
// но это стоит проверять в документации. Для безопасности можем закодировать и их.
// Например, "orderParams%5BshipmentAddress%5D" вместо "orderParams[shipmentAddress]"
ТекстЗапроса = "&orderParams[shipmentAddress]=" + КодированноеЗначениеАдресаОтгрузки +
               "&orderParams[paymentMethod]=2" +
               "&orderParams[shipmentMethod]=1" +
               "&orderParams[shippingDateLast]=1" +
               "&orderParams[transportType]=1" +
               "&positions[0][id]=98989898" +
               "&positions[0][positionParams][comment]=" + КодированноеЗначениеКомментария;

ФрагментЗапроса = "userlogin=" + КодированныйЛогин + "&userpsw=" + КодированныйПароль + ТекстЗапроса;

// Далее код отправки запроса остается прежним
HTTPСоединение = Новый HTTPСоединение("abcp****.public.api.abcp.ru",,,,,, Новый ЗащищенноеСоединениеOpenSSL());
HTTPЗапрос = Новый HTTPЗапрос("/cp/orders/online");
HTTPЗапрос.УстановитьТелоИзСтроки(ФрагментЗапроса);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");

Обратите внимание, что в примере выше мы закодировали только значения. Если документация API ABCP требует кодирования квадратных скобок в именах параметров (например, `orderParams[shipmentAddress]` должно стать `orderParams%5BshipmentAddress%5D`), то нам потребуется закодировать и эти части ключей.

3. Специфические требования API ABCP и документация

Чтобы успешно взаимодействовать с API ABCP, нам необходимо учитывать его специфические требования. Давайте проанализируем важные аспекты:

  1. Использование HTTPS: API ABCP требует защищенного соединения HTTPS. В нашем исходном коде это уже учтено с помощью Новый ЗащищенноеСоединениеOpenSSL(), что является правильным.
  2. Идентификация пользователя: Для большинства операций требуется идентификация с помощью userlogin и MD5-хеша пароля (userpsw). Убедитесь, что ваш пароль правильно хеширован в MD5 перед передачей в userpsw.
  3. Обязательные параметры: При создании заказа параметр shipmentAddress может быть обязательным. Если вы используете самовывоз или по какой-то причине адрес не возвращает значений при запросе basket/shipmentAddresses, необходимо передать shipmentAddress=0. Проверьте этот момент в вашей логике.
  4. Кодировка данных: Обмен данными осуществляется в кодировке UTF-8. Убедитесь, что все строки в 1С, передаваемые в запрос, корректно обрабатываются в этой кодировке.

Мы рекомендуем тщательно изучить актуальную документацию API ABCP для операции /cp/orders/online (или /cp/ts/orders/create, если это более актуальный метод для создания заказов). Убедитесь, что все параметры передаются в правильном формате, с корректными значениями и в соответствии с ожиданиями API, особенно это касается обязательных полей, таких как shipmentAddress и paymentMethod.

4. Использование инструментов отладки

Для отладки HTTP-запросов крайне полезно использовать внешние инструменты. Мы можем воспользоваться расширениями для браузеров, например, Advanced REST Client для Google Chrome, или standalone-приложениями, такими как Postman или Insomnia. Эти инструменты позволяют:

  1. Сформировать запрос: Вы можете вручную указать URL, метод (POST), заголовки (Content-Type: application/x-www-form-urlencoded) и тело запроса.
  2. Отправить запрос: Легко отправить запрос на сервер ABCP.
  3. Проанализировать ответ: Увидеть полный ответ сервера, включая код состояния и тело ответа, что часто дает более детальную информацию, чем просто ошибка 500.

Пошаговый подход к отладке:

  1. Начните с минимально возможного запроса, содержащего только обязательные параметры (логин/пароль и, возможно, один-два других, которые точно работают).
  2. Убедитесь, что этот минимальный запрос работает (получаете не 500 ошибку).
  3. Постепенно добавляйте остальные параметры, каждый раз проверяя работу запроса. Это поможет изолировать конкретный параметр или их комбинацию, которая вызывает ошибку 500.

Итоговое решение: пересматриваем наш код

Принимая во внимание все вышеизложенные рекомендации, мы можем переработать наш исходный код для отправки заказа. Основное изменение — это тщательное URL-кодирование всех значений.


// Шаг 1: Определяем все параметры и их значения
ЛогинПользователя = "api@abcp****"; // Ваш логин
ПарольПользователя = "d07**************9c"; // Ваш MD5-хеш пароля

// Параметры заказа
АдресОтгрузкиGUID = "FD765B07-2B9E-11E9-A2C9-005056802F4C"; // GUID адреса отгрузки
МетодОплаты = "2"; // ID метода оплаты
МетодДоставки = "1"; // ID метода доставки
ПоследняяДатаОтгрузки = "1"; // Дата отгрузки
ТипТранспорта = "1"; // ID типа транспорта

// Позиции заказа (пример для одной позиции)
ИдПозиции0 = "98989898"; // ID позиции
КомментарийПозиции0 = "Тестовый комментарий к позиции с пробелами и спец. символами !@#$%^&*()";

// Шаг 2: Кодируем каждое значение, которое будет частью тела запроса
// Используем КодироватьСтроку с СпособКодированияСтроки.КодировкаURL
КодированныйЛогин = КодироватьСтроку(ЛогинПользователя, СпособКодированияСтроки.КодировкаURL);
КодированныйПароль = КодироватьСтроку(ПарольПользователя, СпособКодированияСтроки.КодировкаURL);

КодированныйАдресОтгрузкиGUID = КодироватьСтроку(АдресОтгрузкиGUID, СпособКодированияСтроки.КодировкаURL);
КодированныйМетодОплаты = КодироватьСтроку(МетодОплаты, СпособКодированияСтроки.КодировкаURL);
КодированныйМетодДоставки = КодироватьСтроку(МетодДоставки, СпособКодированияСтроки.КодировкаURL);
КодированнаяПоследняяДатаОтгрузки = КодироватьСтроку(ПоследняяДатаОтгрузки, СпособКодированияСтроки.КодировкаURL);
КодированныйТипТранспорта = КодироватьСтроку(ТипТранспорта, СпособКодированияСтроки.КодировкаURL);

КодированныйИдПозиции0 = КодироватьСтроку(ИдПозиции0, СпособКодированияСтроки.КодировкаURL);
КодированныйКомментарийПозиции0 = КодироватьСтроку(КомментарийПозиции0, СпособКодированияСтроки.КодировкаURL);

// Шаг 3: Собираем тело запроса, используя закодированные значения
// Важно: квадратные скобки в именах параметров ([]) для ABCP API обычно не кодируются,
// если они используются для обозначения массивов/структур.
// Если бы API требовал их кодирования, то "orderParams[shipmentAddress]" стало бы "orderParams%5BshipmentAddress%5D".
// Проверьте это по документации ABCP. В данном примере оставляем их как есть.
ФрагментЗапроса = "userlogin=" + КодированныйЛогин + "&userpsw=" + КодированныйПароль +
               "&orderParams[shipmentAddress]=" + КодированныйАдресОтгрузкиGUID +
               "&orderParams[paymentMethod]=" + КодированныйМетодОплаты +
               "&orderParams[shipmentMethod]=" + КодированныйМетодДоставки +
               "&orderParams[shippingDateLast]=" + КодированнаяПоследняяДатаОтгрузки +
               "&orderParams[transportType]=" + КодированныйТипТранспорта +
               "&positions[0][id]=" + КодированныйИдПозиции0 +
               "&positions[0][positionParams][comment]=" + КодированныйКомментарийПозиции0;

// Шаг 4: Отправляем HTTP-запрос
HTTPСоединение = Новый HTTPСоединение("abcp****.public.api.abcp.ru",,,,,, Новый ЗащищенноеСоединениеOpenSSL());
HTTPЗапрос = Новый HTTPЗапрос("/cp/orders/online");
HTTPЗапрос.УстановитьТелоИзСтроки(ФрагментЗапроса);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");

Попытка
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос, ИмяВыходногоФайла);    
    КодСостояния = HTTPОтвет.КодСостояния;
    Если КодСостояния = 200 Тогда
        Сообщить("Заказ успешно отправлен. Код состояния: " + КодСостояния);
        // Дополнительная обработка успешного ответа
    Иначе
        ТекстСообщения = "Ошибка при отправке заказа. Код состояния: " + КодСостояния + ". Текст ответа: " + HTTPОтвет.ПолучитьТелоКакСтроку();
        Сообщить(ТекстСообщения);
    КонецЕсли;
Исключение
    ТекстСообщения = "Произошла ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки();
    Сообщить(ТекстСообщения);
    Возврат;
КонецПопытки;

Применяя эти шаги, мы значительно повысим шансы на успешную отправку запроса и минимизируем вероятность возникновения ошибки 500, связанной с некорректным формированием тела запроса. Помните, что детальное изучение документации API ABCP и последовательная отладка — ваши лучшие помощники в решении подобных задач.

← К списку