Проверка параметров HTTP-сервиса 1С: белый список, ошибка 400 и явные умолчания
Иван Недомолков · Опубликовано:
Параметры HTTP-сервиса 1С проверяет только ваш обработчик. Если клиент прислал имя, которого код не читает, платформа ответит так, будто параметра нет, и отработает с умолчанием. Поэтому на входе нужны четыре вещи: сверка имён с белым списком, ответ 400 с названием лишнего параметра, умолчание, которое видно в ответе, и дата, на которую собраны отданные данные. Ниже каждое правило с кодом и способ проверить свой сервис за минуту.
Четыре правила строгого входа
| Правило | Что случится без него | Где в коде |
|---|---|---|
| известные имена перечислены списком | опечатка в имени проходит без звука | цикл по ПараметрыЗапроса до всякой работы |
| неизвестное имя даёт 400 и текст с этим именем | клиент получает 200 и чужие данные | ранний возврат HTTPСервисОтвет(400) |
| умолчание либо самое частое, либо его нет | запрос уходит в источник, о котором клиент не думал | ветка на Неопределено |
| в ответе видно, откуда данные и какого они возраста | устаревший снимок неотличим от свежего | заголовки ответа |
Первые два правила ловят ошибку клиента. Третье определяет, во что эта ошибка превратится, если проверку всё-таки обошли. Четвёртое нужно даже при идеальном запросе: верный параметр не гарантирует свежих данных.
Откуда берётся тихая подмена
Типичный обработчик читает параметр по имени и подставляет значение, если его не прислали:
Функция НайтиДанные(Запрос)
ИмяБазы = Запрос.ПараметрыЗапроса.Получить("db");
Если ИмяБазы = Неопределено Тогда
ИмяБазы = БазаПоУмолчанию(); // сюда попадёт и опечатка
КонецЕсли;
...
Клиент ошибся и написал database=Склад. Получить("db") вернул Неопределено, код ушёл в ветку умолчания, а database никто не прочитал. В ответе код 200 и данные, по форме неотличимые от правильных. Мониторинг здесь бессилен: код успешный, тело непустое, время отклика обычное.
Ветка Неопределено в этом коде обслуживает два разных случая: параметр честно не передан и параметр передан под другим именем. Строгий вход их разводит.
Правило 1 и 2: белый список и ответ 400
Проверка ставится первой строкой обработчика, до обращения к данным. Каждый ключ из ПараметрыЗапроса ищем в списке известных, на первом чужом выходим:
Функция НайтиДанные(Запрос)
Известные = Новый Массив;
Известные.Добавить("db");
Известные.Добавить("query");
Известные.Добавить("limit");
Для Каждого КлючЗначение Из Запрос.ПараметрыЗапроса Цикл
Если Известные.Найти(КлючЗначение.Ключ) = Неопределено Тогда
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Неизвестный параметр: " + КлючЗначение.Ключ);
Возврат Ответ;
КонецЕсли;
КонецЦикла;
// дальше обычная работа
Текст ошибки называет параметр. Человек у клиента исправит опечатку за секунду, увидев Неизвестный параметр: database. Голый код 400 без тела заставит его идти в журнал или писать вам.
Против такой строгости есть рабочий довод: клиенты разных версий шлют разные наборы полей, а прокси и балансировщики иногда дописывают своё, и старый клиент не должен падать на новом сервере. Довод справедлив для полей данных. Для параметров, которые выбирают источник, он не работает. Опечатка в фильтре обходится пустой выдачей, опечатка в имени базы обходится решением, принятым по чужим данным. Если совместимость нужна, держите для таких параметров отдельный строгий список, а остальным позвольте проходить.
Правило 3: умолчание самое частое или никакого
Когда параметр выбора источника не пришёл, вариантов два. Первый: подставить тот источник, к которому идёт почти каждый запрос. Тогда промах по имени почти ничего не стоит. Второй: отказать и попросить указать явно. Третий вариант, умолчание, выставленное когда-то наугад, и есть та мина, из-за которой опечатка меняет смысл запроса.
Если сервис обслуживает несколько равноправных баз, самого частого источника у него нет, и честнее отказать:
ИмяБазы = Запрос.ПараметрыЗапроса.Получить("db");
Если ИмяБазы = Неопределено Тогда
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Не указан параметр db");
Возврат Ответ;
КонецЕсли;
Если умолчание всё же оставили, клиент должен видеть, что оно сработало. Об этом следующее правило.
Правило 4: источник и возраст данных в ответе
Любой ответ, собранный из индекса, кэша, снимка или выгрузки, несёт дату сборки этого снимка. Отдельным заголовком её видно без разбора тела:
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("X-Source-Built",
Формат(ДатаСборкиИсточника, "ДФ=yyyy-MM-ddTHH:mm:ss"));
Рядом логично отдать и имя базы, из которой ответ собран, тем же способом, через Ответ.Заголовки.Вставить. Тогда сработавшее умолчание перестаёт быть невидимым: клиент просил одно, в заголовке другое, и расхождение ловится первым же взглядом. Это самая дешёвая правка из четырёх, одно поле на ответ, и её чаще всего забывают.
Возраст опасен тем, что растёт медленно. Снимок, отставший на несколько процентов, отвечает правильно почти всегда и неправильно изредка, и глазом этого не заметить. Поэтому одной метки мало: ставьте порог возраста и сигнал при его превышении.
Как проверить свой сервис за минуту
- Добавьте к рабочему запросу выдуманный параметр, например
&xyzzy=1. Строгий сервис ответит 400 с именемxyzzy. Ответ 200 значит, что правил 1 и 2 у вас нет. - Уберите параметр выбора источника совсем. Посмотрите, что пришло: отказ или данные, и если данные, то из какой базы. Если из ответа это не понять, правило 4 не выполнено.
- Найдите в ответе дату сборки данных. Нет даты, значит, и у клиента нет способа понять, стоит ли верить ответу.
- Отдельно проверьте пустую выдачу. Отправьте запрос, на который источник обязан вернуть хоть что-то. Если и он даёт ноль, ноль говорит о синтаксисе запроса, данные тут ни при чём.
Четвёртый пункт вырос из той же истории: поиск по коду возвращал ноль на строку со скобкой, ПолучитьОстатки(, и полную выдачу без неё. Для человека и тем более для ИИ-агента пустой ответ звучит как “такого нет”, хотя значит “запрос не понят”.
Как это выглядело на живом сервисе
Правила выше собраны из разбора внутреннего сервиса кодового индекса в крупной розничной сети. Базу в запросах передавали параметром database, сервис знал только db, выбрасывал лишнее и отвечал из базы по умолчанию, причём умолчанием стояла далеко не самая используемая система. Больше трёх месяцев ответы приходили успешные, с путём к файлу и номером строки, только из другого проекта. Сверка с поиском по файлам показала полную точность механики: 261 совпадение из 261, 264 из 264, 182 из 182, 318 функций из 318 при разборе структуры. Точность была настоящей, источник чужим.
Второй слой того же отказа: индекс был собран 17 апреля, разбор шёл 20 июля, разрыв 94 дня, и в ответах про это не было ни слова. После ручного обновления файлов в индексе стало 6 516 вместо 6 252 (плюс 4,2 %), вхождений искомого метода 279 вместо 261 (плюс 6,9 %). Небольшой разрыв и позволил дефекту жить так долго. Индекс с тех пор обновляется таймером, а вот молчаливое игнорирование неизвестных аргументов в том сервисе так и не убрали, так что следующая опечатка пройдёт так же тихо.
Где тот же класс отказа живёт в 1С
HTTP-сервис на встроенном языке тут не единственный. Регламентное задание, которое “обновляет данные каждую ночь”, может быть отключено в этой базе, и тогда любой ответ из обновляемых им данных устаревает без всякой метки. Как убедиться, что задание вообще запускалось, разобрано в статье про регламентное задание, которое не выполняется. Обходом РегламентныеЗадания.ПолучитьРегламентныеЗадания() с чтением ПоследнееЗадание видно, когда каждое из них отработало в последний раз.
Файлы, которые вы отдаёте модели, тоже снимок. Выгрузка метаданных 1С пишет в шапку имя и версию конфигурации, версию платформы и дату снятия, чтобы через два месяца было видно, из какого состояния структура. Имени информационной базы в шапке нет, поэтому выгрузку с теста и с боевой по ней не различить, и имя базы приходится дописывать руками.
Строгий вход решает только часть задачи, когда до обработчика вызов вообще доходит. Если сервис отвечает через раз, а в журнале регистрации пусто, вопрос уже к публикации и пулу веб-сервера, это проверяет чек-ап веб-публикации. Если строгий вход нужно встроить в существующий обмен между базами, не сломав старых клиентов, это задача из нашей практики обмена данными 1С. Когда сервис уже возвращает понятные коды, клиенту остаётся решить, какие из них повторять: это разобрано на примере таймаута обмена с внешним сервисом.
Открытым остаётся вопрос совместимости. Сколько ваших клиентов сегодня шлют параметры, которых сервис не читает, и узнаете ли вы об этом раньше, чем включите 400?