Как исправить ошибки 404 и 400 при получении токена REST API с помощью HTTPЗапроса в 1С?

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

При работе с внешними REST API в 1С одной из наиболее частых задач является получение токена авторизации. Этот токен необходим для последующих запросов к защищенным ресурсам API. Однако в процессе реализации механизма получения токена разработчики часто сталкиваются с различными HTTP-ошибками, такими как 404 (Not Found) или 400 (Bad Request). В этой статье мы подробно разберем типичные ошибки и покажем, как их исправить, на примере получения токена с использованием объекта HTTPЗапрос в 1С.

Рассмотрим ситуацию, когда мы пытаемся получить токен, отправляя POST-запрос на специальный endpoint API, и получаем в ответ код состояния 404, а затем 400. Мы проанализируем причины этих ошибок и пошагово исправим код, чтобы успешно получить токен.

Понимание архитектуры запроса токена OAuth 2.0

Прежде чем углубляться в код, важно понимать общие принципы получения токена OAuth 2.0, особенно при использовании типа предоставления "Resource Owner Password Credentials" (grant_type=password), который подразумевает передачу логина и пароля пользователя.

  1. Endpoint токена: Запрос токена всегда отправляется методом POST на заранее определенный "token endpoint" (конечную точку токена). Адрес этого endpoint должен быть точно указан в запросе.
  2. Тип предоставления (grant_type): Для получения токена мы должны указать, какой "грант" мы используем. В нашем случае это password. Этот параметр передается в теле запроса.
  3. Параметры авторизации: Логин (username) и пароль (password) пользователя также должны быть переданы в теле запроса.
  4. Content-Type заголовка: Когда параметры (grant_type, username, password) передаются в теле запроса в формате key=value&key2=value2, заголовок Content-Type должен быть установлен как application/x-www-form-urlencoded.
  5. Тело запроса: Все параметры в теле запроса должны быть представлены в виде строки ключ1=значение1&ключ2=значение2.
  6. URL-кодирование: Все значения параметров в теле запроса должны быть URL-кодированы. Это критически важно для специальных символов, таких как @, &, +, пробелы и другие, которые могут быть неправильно интерпретированы сервером, если не закодированы.

Исходная проблема и первые шаги отладки (Ошибка 404)

Рассмотрим исходный код, который демонстрирует проблему получения токена:


&НаСервере
Функция Tokens()
    ПараметрыURL = Новый Структура;
    АдресЗапроса = ""; // Здесь будет адрес, но используется POSTЗапрос
    
    // Исходное неверное формирование строки запроса для конструктора HTTPЗапрос
    POSTЗапрос = "POST /api/token HTTP/1.1"; 
    
    Хост = "portal-doctor.eleks.com";
    
    // Переменные для формирования тела запроса (пока не используются)
    username = Объект.Логин + "@@@" + Объект.installation;
    grant_type = "password&username=" + Объект.Логин + "@@@" + Объект.installation + "&password="+Объект.Пароль;
    
    // Инициализация HTTPСоединения
    HTTPСоединение = новый HTTPСоединение(Хост, , , ,, , Новый ЗащищенноеСоединениеOpenSSL);
    
    // Исходные заголовки, включающие параметры, которые должны быть в теле
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("grant_type", "password");
    Заголовки.Вставить("username", "api1CKryvyiRh@@@d8b5df4b-4c01-4eda-9302-2f8328af808a");
    Заголовки.Вставить("password", "7#M0*0Iyl");
    Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
    Заголовки.Вставить("Host", Хост);
    Заголовки.Вставить("Content-Length", "107"); // Примерная длина, может быть некорректной
    
    HTTPЗапрос = Новый HTTPЗапрос(POSTЗапрос, Заголовки); // Здесь проблема с POSTЗапрос
    
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
    
    Если HTTPОтвет.КодСостояния = 200 Тогда
        Ответ = HTTPОтвет.ПолучитьТелоКакСтроку;
        ЧтениеJSON = новый ЧтениеJSON;
        ЧтениеJSON.УстановитьСтроку(Ответ);
        Token = ПрочитатьJSON(ЧтениеJSON, Ложь);
        Сообщить("Ура.");
        Возврат Token;
    Иначе
        ВызватьИсключение "Произошла ошибка обращения к серверу," + "Токен не получен" + Символы.ПС + "Статус ответа сервера: " + HTTPОтвет.КодСостояния;
    КонецЕсли;
КонецФункции

При выполнении этого кода мы получаем ошибку с кодом состояния 404 (Not Found). Выясним причину этой ошибки и исправим ее.

  1. Шаг 1: Корректируем ресурс в конструкторе HTTPЗапрос.

    Основная проблема 404 ошибки в исходном коде заключается в неправильном использовании первого параметра конструктора объекта HTTPЗапрос. Мы передаем туда полную строку HTTP-запроса: "POST /api/token HTTP/1.1". Однако, конструктор HTTPЗапрос ожидает в качестве первого параметра только путь к ресурсу (URL-path), т.е. "/api/token". Метод HTTP (POST), протокол (HTTP/1.1) и хост определяются другими параметрами и методом HTTPСоединение.ОтправитьДляОбработки().

    Давайте исправим строку, определяющую ресурс:

    
    // Было:
    // POSTЗапрос = "POST /api/token HTTP/1.1";
    // HTTPЗапрос = Новый HTTPЗапрос(POSTЗапрос, Заголовки);
            
    // Стало:
    ПутьКРесурсу = "/api/token"; // Только путь к ресурсу
    HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу, Заголовки);
    

    После этого исправления, повторно запустив код, мы заметим, что ошибка 404 исчезла. Однако теперь сервер возвращает другую ошибку: 400 (Bad Request).

Устраняем ошибку 400: Правильное формирование тела запроса

Ошибка 400 (Bad Request) обычно указывает на то, что сам запрос сформирован неправильно, а не на проблемы с доступностью ресурса (как 404) или авторизацией (как 401). В нашем случае, наиболее вероятная причина 400 ошибки заключается в неправильном размещении параметров авторизации (grant_type, username, password).

Как мы уже выяснили, при использовании Content-Type: application/x-www-form-urlencoded, все эти параметры должны передаваться в теле запроса, а не в его заголовках. В исходном коде эти параметры ошибочно помещены в коллекцию Заголовки.

  1. Шаг 2: Удаляем параметры авторизации из заголовков.

    Нам необходимо убрать следующие строки из формирования заголовков:

    
    // Эти строки должны быть удалены из заголовков:
    // Заголовки.Вставить("grant_type", "password");
    // Заголовки.Вставить("username", "api1CKryvyiRh@@@d8b5df4b-4c01-4eda-9302-2f8328af808a");
    // Заголовки.Вставить("password", "7#M0*0Iyl");
    

    Заголовок Content-Length также лучше удалить, так как он может быть некорректным, и сервер сам вычислит его. Если он необходим, его следует вычислять после формирования тела запроса.

  2. Шаг 3: Формируем тело запроса и используем метод УстановитьТелоИзСтроки.

    Теперь, когда мы убрали параметры из заголовков, нам нужно передать их в теле запроса. Для этого мы будем использовать переменную grant_type, которая уже была определена в исходном коде, но не использовалась. Однако, ее определение также требует коррекции.

    Критически важный момент: строка тела запроса должна начинаться с grant_type=, а не просто с password. То есть, каждый параметр должен иметь вид ключ=значение.

    Корректное формирование строки тела запроса будет выглядеть так:

    
    // Корректируем формирование строки для тела запроса
    username_val = Объект.Логин + "@@@" + Объект.installation;
    password_val = Объект.Пароль;
            
    // Важно: каждый параметр должен быть в формате "ключ=значение"
    ТелоЗапросаСтрока = "grant_type=password"
                        + "&username=" + username_val
                        + "&password=" + password_val;
            
    // Передаем сформированную строку в тело запроса
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаСтрока);
    

    Метод HTTPЗапрос.УстановитьТелоИзСтроки() по умолчанию отправляет данные в кодировке UTF-8, что в большинстве случаев является правильным выбором для REST API.

  3. Шаг 4: Обеспечиваем URL-кодирование специальных символов.

    В нашем примере логин пользователя содержит символ @, а пароль может содержать другие специальные символы (#, *, & и т.д.). Эти символы, если они не закодированы, могут нарушить структуру URL-кодированного тела запроса. Например, символ & является разделителем параметров, а = - разделителем ключа и значения.

    Для корректной передачи таких значений, мы должны их URL-кодировать. В 1С для этого используется функция КодироватьURL():

    
    // Использование КодироватьURL для значений параметров
    username_val = КодироватьURL(Объект.Логин + "@@@" + Объект.installation);
    password_val = КодироватьURL(Объект.Пароль);
            
    ТелоЗапросаСтрока = "grant_type=password"
                        + "&username=" + username_val
                        + "&password=" + password_val;
            
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаСтрока);
    

    Это гарантирует, что все специальные символы будут правильно интерпретированы сервером и не приведут к ошибке 400.

Полный пример исправленного кода для получения токена

Теперь, собрав все исправления, мы получаем рабочий код для получения токена. Обратите внимание, что мы также учли рекомендации по безопасному хранению учетных данных (логин и пароль), не вставляя их напрямую в код, а используя переменные Объект.Логин и Объект.Пароль.


&НаСервере
Функция Tokens()
    // 1. Определение хоста и пути к ресурсу
    Хост = "portal-doctor.eleks.com";
    ПутьКРесурсу = "/api/token"; // Исправлено: только путь к ресурсу
    
    // 2. Формирование тела запроса с URL-кодированием
    // Предполагаем, что Объект.Логин, Объект.installation и Объект.Пароль содержат необходимые данные
    ЛогинПользователя = Объект.Логин; // Например, "api1CKryvyiRh"
    ИдентификаторУстановки = Объект.installation; // Например, "d8b5df4b-4c01-4eda-9302-2f8328af808a"
    ПарольПользователя = Объект.Пароль; // Например, "7#M0*0Iyl"
    
    // Собираем полное имя пользователя в требуемом формате, затем кодируем его
    ПолноеИмяПользователя = ЛогинПользователя + "@@@" + ИдентификаторУстановки;
    ЗакодированноеИмяПользователя = КодироватьURL(ПолноеИмяПользователя);
    ЗакодированныйПароль = КодироватьURL(ПарольПользователя);
    
    // Формируем строку тела запроса в формате application/x-www-form-urlencoded
    // Каждый параметр должен быть в формате "ключ=значение"
    ТелоЗапросаСтрока = "grant_type=password" 
                        + "&username=" + ЗакодированноеИмяПользователя
                        + "&password=" + ЗакодированныйПароль;

    // 3. Инициализация HTTPСоединения с защищенным соединением для HTTPS
    HTTPСоединение = Новый HTTPСоединение(Хост, , , , , , Новый ЗащищенноеСоединениеOpenSSL);
    
    // 4. Формирование заголовков запроса
    Заголовки = Новый Соответствие;
    // Важно: "Content-Type" должен быть "application/x-www-form-urlencoded"
    Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
    // Заголовок "Host" обычно автоматически добавляется HTTPСоединением, но можно явно указать
    Заголовки.Вставить("Host", Хост);
    // Заголовки "grant_type", "username", "password" удалены, так как они в теле
    // Заголовок "Content-Length" также не нужен, если используется УстановитьТелоИзСтроки,
    // так как 1С сама его рассчитает.
    
    // 5. Создание HTTPЗапроса
    HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу, Заголовки);
    
    // 6. Установка тела запроса из сформированной строки
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаСтрока, КодировкаТекста.UTF8); // Явно указываем кодировку UTF8

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

Общие рекомендации по отладке REST API запросов в 1С

При работе с REST API и возникновении проблем с получением токенов или других данных, мы рекомендуем следовать следующим практическим советам:

  1. Используйте Postman (или аналогичные инструменты):

    Postman является незаменимым инструментом для тестирования REST API. Мы всегда начинаем отладку с него. Он позволяет отправлять запросы и просматривать ответы в удобном формате. Если запрос работает в Postman с кодом 200, это означает, что сам API доступен, и вы используете правильные параметры, заголовки и тело запроса. В Postman есть функция "Code" (кнопка обычно справа), которая может сгенерировать пример кода запроса для различных языков и форматов, включая обычный HTTP. Это позволяет нам увидеть точное формирование запроса, включая URL-кодирование, и сравнить его с тем, что мы отправляем из 1С.

  2. Внимательно читайте документацию API:

    Каждое REST API имеет свою специфику. Endpoints, требуемые параметры, их формат, расположение (заголовки или тело запроса), а также тип авторизации могут отличаться. Документация — наш лучший друг в этом процессе.

  3. Логируйте ответы сервера:

    Когда мы получаем ошибку (например, 400, 401, 500), мы всегда должны анализировать не только HTTPОтвет.КодСостояния, но и HTTPОтвет.ПолучитьТелоКакСтроку(). Сервер часто возвращает в теле ответа подробное описание ошибки в формате JSON или XML, что значительно ускоряет диагностику проблемы. Например, для ошибки 400, тело ответа может содержать сообщение типа {"error": "invalid_grant", "error_description": "Bad credentials"}.

  4. Используйте ЗащищенноеСоединениеOpenSSL для HTTPS:

    Подавляющее большинство REST API работают через HTTPS. Убедитесь, что вы правильно инициализируете HTTPСоединение с Новый ЗащищенноеСоединениеOpenSSL, как показано в примере кода. Без этого вы не сможете установить защищенное соединение.

  5. Безопасность учетных данных:

    Никогда не хардкодьте (жестко не прописывайте) логины и пароли в коде. Получайте их из настроек конфигурации, защищенных хранилищ или пользовательского ввода.

  6. URL-кодирование:

    Мы снова подчеркиваем важность URL-кодирования для всех значений параметров, передаваемых в теле запроса (особенно для application/x-www-form-urlencoded) и в параметрах URL. Используйте функцию КодироватьURL() для любых строковых значений, которые могут содержать специальные символы.

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

← На главную