Как правильно передать дату в HTTP-запрос, выполнить оптимизированный отбор данных и вернуть ответ в JSON?

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

При разработке HTTP-сервисов в 1С часто возникает задача получения данных за определенный период. Казалось бы, задача тривиальная: получить параметры (год, месяц, день), найти документы и вернуть их список. Однако на этом пути нас подстерегают три основные проблемы: типизация входящих данных, производительность запроса к базе данных и корректная сериализация ответа. Давайте подробно разберем каждую из них и составим правильный алгоритм решения.

Этап 1. Обработка параметров HTTP-запроса

В рассматриваемом случае параметры передаются в URL отдельными частями: год, месяц и день. Первая ошибка, с которой мы сталкиваемся — это несоответствие типов.

Метод Запрос.ПараметрыURL.Получить("ИмяПараметра") всегда возвращает значение типа Строка (или Неопределено, если параметр не передан). Конструктор даты или параметры запроса часто ожидают Число или Дату. Если передать строку напрямую в функцию создания даты, возникнет ошибка.

Рассмотрим, как правильно получить и преобразовать данные:


// Получаем параметры как строки
ГодСтрока   = Запрос.ПараметрыURL.Получить("God");
МесяцСтрока = Запрос.ПараметрыURL.Получить("Mesac");
ДеньСтрока  = Запрос.ПараметрыURL.Получить("Den");

// Проверяем заполненность и преобразуем в Число
Попытка
    ГодЧисло   = Число(ГодСтрока);
    МесяцЧисло = Число(МесяцСтрока);
    ДеньЧисло  = Число(ДеньСтрока);
    
    // Формируем дату 1С
    ИскомаяДата = Дата(ГодЧисло, МесяцЧисло, ДеньЧисло);
Исключение
    // Обработка ошибки, если переданы некорректные данные
    Ответ = Новый HTTPСервисОтвет(400);
    Ответ.УстановитьТелоИзСтроки("Неверный формат даты");
    Возврат Ответ;
КонецПопытки;

Таким образом, мы гарантируем, что у нас есть переменная ИскомаяДата правильного типа, с которой можно работать дальше.

Этап 2. Оптимизация запроса к базе данных

Это, пожалуй, самый важный момент. В исходном коде использовалась следующая конструкция:


ГДЕ
    ГОД(ПоступлениеТоваров.Дата) = &God
    И МЕСЯЦ(ПоступлениеТоваров.Дата) = &Mesac
    И ДЕНЬ(ПоступлениеТоваров.Дата) = &Den

Почему так делать нельзя? Использование функций (ГОД, МЕСЯЦ, ДЕНЬ, НАЧАЛОПЕРИОДА) над полем таблицы в секции ГДЕ отключает использование индексов SQL. СУБД вынуждена сканировать всю таблицу (Full Scan), вычислять значения для каждой строки и сравнивать их. На больших объемах данных это приведет к критическому падению производительности.

Правильное решение: использовать диапазон дат. Нам нужно отобрать документы за конкретные сутки. Для этого мы определим начало и конец этого дня и используем оператор МЕЖДУ. В этом случае SQL-сервер сможет использовать индекс по полю Дата.

Подготовим параметры для запроса:


// Определяем границы дня
НачалоДня = НачалоДня(ИскомаяДата);
КонецДня  = КонецДня(ИскомаяДата);

И изменим текст запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ПоступлениеТоваров.Номер КАК Номер,
    |   ПоступлениеТоваров.Дата КАК Дата,
    |   ПоступлениеТоваров.Контрагент.Представление КАК Контрагент,
    |   ПоступлениеТоваров.Склад.Представление КАК Склад,
    |   ПоступлениеТоваров.СуммаДокумента КАК Сумма
    |ИЗ
    |   Документ.ПоступлениеТоваров КАК ПоступлениеТоваров
    |ГДЕ
    |   ПоступлениеТоваров.Дата МЕЖДУ &НачалоДня И &КонецДня";

Запрос.УстановитьПараметр("НачалоДня", НачалоДня);
Запрос.УстановитьПараметр("КонецДня", КонецДня);

Этап 3. Формирование JSON и отправка ответа

Третья распространенная проблема — ошибки при сериализации в JSON, которые часто возникают при работе с внешними API, например, с авторизацией через RSA. Разберем основные нюансы:

  1. Для записи JSON в строку необходимо явно вызвать метод УстановитьСтроку() у объекта ЗаписьJSON.
  2. Заголовки HTTP-ответа должны быть корректными. Тип контента text/plainapplication/json является ошибочным (смешаны два типа). Правильный вариант: application/json.
  3. При использовании НастройкиСериализацииJSON ручное преобразование дат в строку часто не требуется, платформа сделает это сама в формат ISO.

Соберем данные в массив структур и сериализуем их (чтобы не дублировать логику, можно использовать готовые процедуры из общего модуля):


Выборка = Запрос.Выполнить().Выбрать();
МассивДанных = Новый Массив;

Пока Выборка.Следующий() Цикл
    СтруктураСтроки = Новый Структура;
    СтруктураСтроки.Вставить("Number", Выборка.Номер);
    СтруктураСтроки.Вставить("Date", Выборка.Дата); // 1С сама преобразует дату в строку JSON
    СтруктураСтроки.Вставить("Partner", Выборка.Контрагент);
    СтруктураСтроки.Вставить("Warehouse", Выборка.Склад);
    СтруктураСтроки.Вставить("Amount", Выборка.Сумма);
    
    МассивДанных.Добавить(СтруктураСтроки);
КонецЦикла;

// Настройка записи JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(); // Важно: пишем в память (строку)

// Параметры форматирования дат
НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата; // Формат ISO 8601

// Записываем массив
ЗаписатьJSON(ЗаписьJSON, МассивДанных, НастройкиСериализации);

// Получаем итоговую строку
СтрокаJSON = ЗаписьJSON.Закрыть();

Итоговый код HTTP-метода

Теперь объединим все исправленные части в один готовый блок кода, который можно использовать в модуле HTTP-сервиса или в составе универсальных библиотек для разработки API.


Функция ПолучитьДокументы(Запрос)
    
    // 1. Получение и валидация параметров
    ГодStr   = Запрос.ПараметрыURL.Получить("God");
    МесStr   = Запрос.ПараметрыURL.Получить("Mesac");
    ДеньStr  = Запрос.ПараметрыURL.Получить("Den");
    
    Попытка
        ДатаЗапроса = Дата(Число(ГодStr), Число(МесStr), Число(ДеньStr));
    Исключение
        Ответ = Новый HTTPСервисОтвет(400);
        Ответ.УстановитьТелоИзСтроки("Ошибка формата даты. Ожидаются числа: God, Mesac, Den");
        Возврат Ответ;
    КонецПопытки;
    
    // 2. Выполнение запроса (Оптимизировано через МЕЖДУ)
    ЗапросSQL = Новый Запрос;
    ЗапросSQL.Текст = 
        "ВЫБРАТЬ
        |   ПоступлениеТоваров.Номер КАК Номер,
        |   ПоступлениеТоваров.Дата КАК Дата,
        |   ПоступлениеТоваров.Контрагент.Представление КАК Контрагент,
        |   ПоступлениеТоваров.СуммаДокумента КАК Сумма
        |ИЗ
        |   Документ.ПоступлениеТоваров КАК ПоступлениеТоваров
        |ГДЕ
        |   ПоступлениеТоваров.Дата МЕЖДУ &НачалоДня И &КонецДня";
        
    ЗапросSQL.УстановитьПараметр("НачалоДня", НачалоДня(ДатаЗапроса));
    ЗапросSQL.УстановитьПараметр("КонецДня", КонецДня(ДатаЗапроса));
    
    Результат = ЗапросSQL.Выполнить();
    Выборка = Результат.Выбрать();
    
    МассивРезультат = Новый Массив;
    Пока Выборка.Следующий() Цикл
        Данные = Новый Структура("Number, Date, Partner, Amount");
        ЗаполнитьЗначенияСвойств(Данные, Выборка);
        МассивРезультат.Добавить(Данные);
    КонецЦикла;
    
    // 3. Сериализация в JSON
    Запись = Новый ЗаписьJSON;
    Запись.УстановитьСтроку();
    
    Настройки = Новый НастройкиСериализацииJSON;
    Настройки.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата;
    
    ЗаписатьJSON(Запись, МассивРезультат, Настройки);
    ТекстJSON = Запись.Закрыть();
    
    // 4. Формирование ответа
    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.Заголовки.Вставить("Content-type", "application/json; charset=utf-8");
    Ответ.УстановитьТелоИзСтроки(ТекстJSON);
    
    Возврат Ответ;
    
КонецФункции

Используя этот подход, мы обеспечиваем высокую скорость работы запроса благодаря индексам, корректную обработку входящих данных и валидный JSON-ответ для клиента.

← На главную