Вводные:
1. Стандартная система обработки запросов через HTTP сервис.
Цель:
Получить общую цепочку обработки запроса с возможностью
1. включения обработчиков в любой точке конвейера
2. обработки URL с помощью regexp
3. обработки исключений исполнения кода.
Реализация:
В HTTP сервисе создаем один "Шаблон URL" с шаблоном "/*", в нем создаем обработчик с "HTTP-метод" = "Любой".
Функция ШаблонURLОбщийОбщийОбработчик(Запрос)
Возврат ph_srv_ОбработчикHTTPСервиса.ОбработатьHTTPЗапрос("MangoOfficeApi", Запрос);
КонецФункции
В общем модуле реализуем обработку
Функция ОбработатьHTTPЗапрос
Функция ОбработатьHTTPЗапрос(ИмяAPI, Запрос) Экспорт
Отказ = Ложь;
СтруктураОтвет = ПолучитьСтруктураОтветПоУмолчанию();
СтруктураМаршрут = ПолучитьМаршрутИПараметрыАдреса(ИмяAPI, Запрос);
Если ПустаяСтрока(СтруктураМаршрут.Маршрут) Тогда
СтруктураОтвет.Ошибки.Вставить("message", "Адрес не найден");
СтруктураОтвет.КодСостояния = 404;
Возврат СформироватьОтвет(Запрос, СтруктураОтвет);
КонецЕсли;
Попытка
ВыполнитьМаршрут(ИмяAPI, СтруктураМаршрут.Маршрут, СтруктураМаршрут.ПараметрыАдреса, СтруктураОтвет, Запрос, Отказ);
Исключение
СтруктураОтвет.Ошибки.Вставить("message", "Внутренняя ошибка выполнения");
СтруктураОтвет.КодСостояния = 500;
ЗаписатьЗапросВЖурналРегистрации("ОшибкаВыполнения", ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
КонецПопытки;
Если Не Отказ Тогда
СтруктураОтвет.КодСостояния = 200;
КонецЕсли;
Возврат СформироватьОтвет(Запрос, СтруктураОтвет);
КонецФункции
Сначала формирую структуру ответа, чтобы в дальнейшем её стандартно сериализовать в ответ.
Функции по структуре ответа
Функция ПолучитьСтруктураОтветПоУмолчанию() Экспорт
СтруктураОтвет = Новый Структура;
СтруктураОтвет.Вставить("КодСостояния", 400);
СтруктураОтвет.Вставить("Ошибки", Новый Соответствие);
СтруктураОтвет.Вставить("Данные", Новый Соответствие);
Возврат СтруктураОтвет;
КонецФункции
Функция СформироватьОтвет(Запрос, СтруктураОтвет)
Если СтруктураОтвет.Ошибки.Количество() > 0 Тогда
Если СтруктураОтвет.КодСостояния = 200 Тогда
СтруктураОтвет.КодСостояния = 400;
КонецЕсли;
СтрокаОтвет = В_JSON(СтруктураОтвет.Ошибки);
Иначе
СтрокаОтвет = В_JSON(СтруктураОтвет.Данные);
КонецЕсли;
Ответ = Новый HTTPСервисОтвет(СтруктураОтвет.КодСостояния);
Ответ.Заголовки.Вставить("Content-type", "application/json");
Ответ.УстановитьТелоИзСтроки(СтрокаОтвет);
Возврат Ответ;
КонецФункции
При желании можно добавить нужную сериализацию.
Далее разбираю строку URL и ищу маршрут. Так же вычисляю значения параметров URL строки. При формирование таблицы маршрутов и структуры паттернов шаблона запрашиваю значения из модуля с реализацией обработки маршрута.
Функции реализации поиска маршрута
Функция ПолучитьМаршрутИПараметрыАдреса(ИмяAPI, Запрос)
СтруктураОтвет = Новый Структура;
СтруктураОтвет.Вставить("Маршрут", "");
СтруктураОтвет.Вставить("ПараметрыАдреса", Новый Соответствие);
НастройкиПараметровАдреса = ПолучитьНастройкиПараметровАдреса(ИмяAPI);
Для Каждого СтрТ Из ПолучитьТаблицуМаршрутов(ИмяAPI) Цикл
Если Запрос.HTTPМетод = СтрТ.Метод И ПроверитьСоответствиеПолучитьПараметрыСтроки(Запрос.ОтносительныйURL, СтрТ.Шаблон, СтруктураОтвет.ПараметрыАдреса, НастройкиПараметровАдреса) Тогда
СтруктураОтвет.Маршрут = СтрТ.ИмяМаршрута;
КонецЕсли;
КонецЦикла;
Возврат СтруктураОтвет;
КонецФункции
//--
Функция ПроверитьСоответствиеПолучитьПараметрыСтроки(Адрес, Шаблон, ПараметрыАдреса, НастройкиПараметровАдреса)
АдресСоответвтвуетШаблону = Ложь;
МассивПараметровШаблона = Новый Массив;
Паттерн = ПолучитьПаттернШаблона(Шаблон, НастройкиПараметровАдреса, МассивПараметровШаблона);
МассивПараметровАдреса = Новый Массив;
Если ph_srv_regexp.ПроверитьПолучитьПодстроки(Адрес, Паттерн, МассивПараметровАдреса) Тогда
АдресСоответвтвуетШаблону = Истина;
Для к = 0 По МассивПараметровШаблона.ВГраница() Цикл
ПараметрыАдреса.Вставить(МассивПараметровШаблона[к], МассивПараметровАдреса[к]);
КонецЦикла;
КонецЕсли;
Возврат АдресСоответвтвуетШаблону;
КонецФункции
Функция ПолучитьПаттернШаблона(Шаблон, СтруктураНастроекПаттерна, МассивПараметровШаблона)
Паттерн = Шаблон;
ПараметрыШаблона = Новый Массив;
Если ph_srv_regexp.ПроверитьПолучитьПодстроки(Шаблон, "\{([a-z-_]+)\}", ПараметрыШаблона) Тогда
Для каждого СтрП Из ПараметрыШаблона Цикл
ЗначениеПаттерна = "";
Если НЕ СтруктураНастроекПаттерна.Свойство(СтрП, ЗначениеПаттерна) Тогда
ЗначениеПаттерна = "[a-z]+";
КонецЕсли;
Паттерн = СтрЗаменить(Паттерн, "{" + СтрП + "}" , "(" + ЗначениеПаттерна + ")");
МассивПараметровШаблона.Добавить(СтрП);
КонецЦикла;
КонецЕсли;
Паттерн = Паттерн + "/?";
Возврат Паттерн;
КонецФункции
Функции получения таблицы маршрутов и паттернов параметров
Функция ПолучитьНастройкиПараметровАдреса(ИмяAPI)
СтруктураНастроекПаттерна = Новый Структура;
СтруктураНастроекПаттерна.Вставить("guid", "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}");
Если ИмяAPI = "MangoOfficeApi" Тогда
ph_srv_ОбработчикHTTPСервиса_MangoOfficeApi.ДобавитьНастройкиПаттерна(СтруктураНастроекПаттерна);
КонецЕсли;
Возврат СтруктураНастроекПаттерна;
КонецФункции
Функция ПолучитьТаблицуМаршрутов(ИмяAPI)
ТаблицаМаршрутов = Новый ТаблицаЗначений;
ТаблицаМаршрутов.Колонки.Добавить("Метод");
ТаблицаМаршрутов.Колонки.Добавить("Шаблон");
ТаблицаМаршрутов.Колонки.Добавить("ИмяМаршрута");
ДобавитьМаршрутВТаблицу(ТаблицаМаршрутов, "GET", "/ping", "ping");
Если ИмяAPI = "MangoOfficeApi" Тогда
ph_srv_ОбработчикHTTPСервиса_MangoOfficeApi.ДобавитьМаршруты(ТаблицаМаршрутов);
КонецЕсли;
Возврат ТаблицаМаршрутов;
КонецФункции
Процедура ДобавитьМаршрутВТаблицу(ТаблицаМаршрутов, Метод, Шаблон, ИмяМаршрута) Экспорт
СтрМ = ТаблицаМаршрутов.Добавить();
СтрМ.Метод = Метод;
СтрМ.Шаблон = Шаблон;
СтрМ.ИмяМаршрута = ИмяМаршрута;
КонецПроцедуры
Поиск идет по таблице значений в которой фиксирую шаблон например "/hello/{guid}". Перед поиском шаблон преобразую в regexp строку заменой {} на паттерны описанные в "СтруктураНастроекПаттерна". В результате получаем гибкость в фильтрации запросов.
Для разбора используется реализация regexp через "VBScript.RegExp".
Реализация работы с regexp
Функция ПроверитьПолучитьПодстроки(СтрокаДанных, Паттерн, МассивПодсток, ИгнорироватьРегистр = Истина, ПоискВсехВхождений = Истина, МногострочныйРежим = Ложь, Отказ = Ложь) Экспорт
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = ИгнорироватьРегистр;
RegExp.Global = ПоискВсехВхождений;
RegExp.MultiLine = МногострочныйРежим;
Попытка
RegExp.Pattern = Паттерн;
Matches=RegExp.Execute(СтрокаДанных);
Отказ = Истина;
Исключение
Возврат Ложь;
КонецПопытки;
ЧислоВхождений=Matches.Count();
Если ЧислоВхождений = 0 Тогда
Возврат Ложь;
КонецЕсли;
Для к = 0 По ЧислоВхождений-1 Цикл
Match = Matches.Item(к);
SubMatches = Match.SubMatches;
Если SubMatches.Count() > 0 Тогда
МассивПодсток.Добавить(SubMatches.Item(0));
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Если маршрут найден то управление передается в модуль реализации бизнес логики.
Процедура ВыполнитьМаршрут(ИмяAPI, Маршрут, ПараметрыАдреса, СтруктураОтвет, Запрос, Отказ)
Если Маршрут = "ping" Тогда
СтруктураОтвет.Данные.Вставить("message", "ОК");
Возврат;
КонецЕсли;
Если ИмяAPI = "MangoOfficeApi" Тогда
ph_srv_ОбработчикHTTPСервиса_MangoOfficeApi.ВыполнитьМаршрут(Маршрут, ПараметрыАдреса, СтруктураОтвет, Запрос, Отказ);
КонецЕсли;
КонецПроцедуры
Функция "ВыполнитьМаршрут" намеренно обернута в "Попытка Исключение" для обработки ошибок, клиенту знать состав ошибки не нужно и в ЖР их нужно фиксировать.
Валидацию, аутентификацию, декодирование тела запроса можно делать централизовано добавив функции в начало функции "ВыполнитьМаршрут".
Сборки (cfe) нет, т.к. пока не придумал как её организовать в формате "поставил-попробовал", все ключевые функции в статье под спойлерами.
Благодарю за внимание.
P.S.
Если есть хорошая реализация валидации входящих данных, то буду рад посмотреть и включить в статью. С XDTO пока не получилось реализовать.