В прошлой статье мы рассказывали, как у нас в банке работает платформа WSO2. Мы предоставляем ее как сервис, как интеграционный слой, следим за его стабильностью, а разработкой на платформе занимаются уже команды из подразделений. Они работают на разных языках — Java, C# и т.д. — и обращаются к нам по необходимости за консультациями, потому что это не их основной стек разработки. Проанализировав обращения, мы решили сделать несколько инструкций по разработке на WSO2, которые охватили 80% всех вопросов, что приходят от команд. Теперь хотим поделиться наработками со всеми и начнем с разработки REST API-сервиса на WSO2. Бонус для самых терпеливых — в конце поста. Для разработки на WSO2 нам потребуется: WSO2 Integration Studio — среда разработки. На момент написания статьи актуальна версия 8.0.0, рекомендую обновиться до нее, поскольку в ней исправлено много ошибок прошлых версий. Также советую поставить все апдейты версии через Help -> Check Updates; Oracle Java JDK 1.8; Apache Maven; Git + опционально Fork или Source Tree (графическая IDE); Docker; WSO2 Microintegrator; Postman, JMeter, SoapUI (хотя можно обойтись curl и консолью, если удобно). Итак, запускаем WSO2 Integration Studio. Создаем новый проект (File – New – Integration Project), указываем название проекта и выбираем необходимые модули. Чуть подробней о модулях. Модули ESBConfig и Composite Exporter должны быть выбраны всегда, так как это основные компоненты. Registry Resources необходим, когда требуется хранить в проекте файлы WSDL/XSD/Swagger/OpenAPI и др. В принципе эти три модуля обязательны для создания REST API-сервиса.Connector Exporter нужен, когда требуется интегрироваться с одним из внешних API, которые доступны как библиотеки (список коннекторов можно найти на сайте WSO2). Также опциональны Docker и Kubernetes, их функция понятна из названия. Теперь указываем параметры проекта и заканчиваем предварительную настройку: Добавляем в каждый из отмеченных ниже каталогов пустой файл с именем ".gitkeep". Это необходимо, чтобы Git добавил их все в контроль версий, несмотря на то что они пустые. Далее создаем обработчики для входящих запросов и ошибок. Выберите нужную папку и в контекстном меню нажмите New Sequence. Создадим обработчик FaultSequence, который будет перехватывать исключения, по аналогии с tryCatch в языках программирования. Платформа должна обрабатывать и логировать все ошибки, а также отдавать клиенту соответствующие сообщения. В противном случае он будет отваливаться по таймауту. Вот пример кода FaultSequence обработчика: Далее создаем inbound-обработчик, который будет обрабатывать входящие запросы — например, выставлять параметры property, environment. В параметре  ENV_SERVICE_NAME важно указать название сервиса, оно же будет использоваться в LoggerService для записи логов в отдельный файл. А в параметре onError добавить ссылку на ранее созданный обработчик ошибок, в нашем случае MOEX_FaultSequence. LoggerService — это плагин нашей собственной разработки, его мы используем вместо стандартного плагина, который не подходит под наши требования. Вместо ClassMediator можно использовать стандартный LogMediator. Пример InboundSequence обработчика:  Теперь нам нужен обработчик, который будет заносить в лог факт обработки запроса. Можно добавить здесь и другие возможности, но для примера мы ограничимся только логированием. Здесь также важно в параметре "onError" указать ссылку на ранее созданный обработчик ошибок, onError="MOEX_FaultSequence", чтобы ошибки OutboundSequence обработчика не потерялись.  Пример OutboundSequence обработчика: Поскольку мы будем создавать несколько API, шаблоны сильно упростят нам жизнь, избавив от лишней копипасты. Для начала — шаблон для вызова внешнего сервиса: Выбираем тип шаблона Sequence Template: Пример шаблона вызова: В этом шаблоне предусмотрена проверка наличия конфигурационного файла (property), откуда мы берем значение версии микроинтегратора WSO2. Если файла нет, значение берется из environment. В конце мы логируем запрос и вызываем веб-сервис. Через контекстное меню выбираем New Endpoint в соответствующей папке: В списке предлагается много вариантов, нас интересует HTTP Endpoint: Хорошее правило — делать столько endpoint, сколько методов у вас в REST-сервисе. Конечно, будет работать и с одним, но если он начнет тормозить, это повлияет на всё. Разные методы имеют разную частоту запросов, и если при работе через один endpoint свободные соединения забьются из-за медлительности какого-то метода, то остальные методы тоже начнут тормозить. Больше endpoint — больше гибкости в настройке системы. Пример реализации endpoint: Здесь важно указать метод http, шаблон адреса, на который будет отправляться запрос (та же переменная, что сформирована в шаблоне вызова). В зависимости от требований системы вы определяете таймаут и прописываете, при каких ошибках делать повторные запросы и при каких условиях помечать endpoint как недоступный. Так реализуется паттерн circuit breaker, или предохранитель. После создания endpoint с методом get нужно создать аналогичный, но уже с методом post, put, patch, delete или другим, в зависимости от требований к сервису. Подготовительная работа завершена, время создавать сам API. Выбираем New REST API и получаем несколько вариантов: создать API с нуля, генерировать через Swagger или импортировать. Для начала рассмотрим первый вариант: Указываем параметры: Name: MOEXService_APIContext: /moex/v{version} — сразу указываем версионирование сервиса, при отсутствии обратной совместимости это необходимо. Букву «v» добавляем для единообразия, так советует вендор.Version: 1.0 или актуальная для вас Вот пример API: Здесь мы вызываем обработчик входящего запроса, выставляем параметры environment и логирования, логируем запрос, вызываем бэкенд через ранее созданный шаблон, затем указываем endpoint и адрес, на который отправить запрос. Все параметры вы задаете в зависимости от своего проекта. В нашем случае мы также проверяем успех запроса. Фильтруем статус ответа, если пришел ответ "200", то все хорошо и мы логируем ожидаемый JSON, если нет, логируем все тело запроса в виде бинарных данных. Несколько важных моментов: Каждый тег <resource> описывает один REST-метод.  Атрибут url-mapping содержит относительный URI, по которому клиент будет обращаться к WSO2 . В параметре SetEndpointURL указывается название переменной для вытягивания URI конечного сервиса из конфигурации.  В параметре SetEndpointURL_Suffix указывается путь к методу относительно URI конечного сервиса. У нас нет значения переменной, куда отправить запрос, и это нормально, потому что конфигурацию мы выносим отдельно от кода. WSO2 поддерживает отдельные файлы конфигурации, где мы пропишем нужный адрес. Конфигурационные параметры сервиса можно хранить в EnvironmentVariables или в специальном конфигурационном файле. Мы используем конфигурационный файл file.properties. Пример конфигурации для нашего сервиса: Другой способ создания API — через Swagger (“Generate API using Swagger definition”). Здесь мы указываем путь к swagger-файлу и затем к модулю, который будет его хранить. В этом случае все методы подтянутся автоматически, но все равно нужно будет их настроить. Хорошая практика — указывать в API ссылку на swagger-файл, чтобы через get-запрос клиенты могли получать спецификацию напрямую из сервиса. WSO2 Integration Studio поставляется со встроенным микроинтегратором. Он не особо функциональный, но для отладки его возможностей достаточно. Рекомендую все таки использовать Docker-образы с последними fix-pack, так как встроенный (embedded) wso2mi в IDE не содержит никаких исправлений, что может привести к проблемам/ошибкам при разработке. Начинаем сборку. Открывает pom-файл в модуле CompositeExporter, и выбираем, какие файлы должны попасть в сборку: Теперь собираем проект с помощью Maven. Идем в каталог с проектом и запускаем сборку: Если сборка прошла успешно, должно появиться вот такое сообщение: Дальнейшие действия зависят от вашего конкретного проекта. Мы, например, пишем интеграционные тесты с использованием Postman и заглушки для BackendService с Wiremock. Перед коммитом стоит добавить в файл исключений ".gitignore" все target-каталоги, в нашем примере это В качестве бонуса расскажем о требованиях к разработке, которые мы сформировали внутри команды. По опыту, их выполнение помогает предотвратить немало проблем в работе с WSO2. В первую очередь необходимо применять повторно используемые SequenceMediator, так как это упрощает понимание и повышает читабельность кода. В проекте должны быть отдельные Sequence для: валидации/парсинга входящего сообщения (Validation Handling), если применимо; для обработки входящего сообщения/запроса (Input Handling), с реализацией общих механизмов для логирования и установки глобальных параметров (environment); для обработки исходящего сообщения/ответа (Output Handling); для обработки и формирования сообщения об ошибке (Fault/Error Handling); для обработки некорректного метода или URL, если применимо. Вызов внешнего API необходимо также сохранить в Sequence, что важно при использовании нескольких вызовов последовательно. Блок <faultSequence/> не должен быть пустым: обязательно нужно делать обработку ошибок в каждом сервисе. Необходимо придерживаться единых правил именования объектов (название проекта, API, Endpoint, Sequence и др.) — у WSO2 есть отдельная таблица на эту тему, в том же документе можно найти другие хорошие советы.  Сообщения об ошибке должны иметь единую структуру в зависимости от формата обмена сервиса. JSON: XML: SOAP 1.1: После получения ответа от Endpoint нужно проверять код ответа HTTP_SC и на основании него формировать ответ клиенту. Если код ответа не 200, 201 и 202, необходимо формировать ошибку.  Для установки параметров нужно использовать именно медиатор PropertyGroup. Медиатор Property допускается только в том случае, когда устанавливается один параметр.  При вызове Endpoint необходимо предусмотреть настройки повторных запросов в случае ошибок. В каждом сервисе обязательно наличие GET-запроса /health, который возвращает HTTP или ошибку 200. И напоследок — ряд общих требований к процессам и оформлению: наличие интеграционных тестов и логирования входящих/исходящих запросов ;  хранение параметров URL, Login, Password в конфигурационных файлах; доступность документации Swagger/OpenAPI версии не ниже 3.0.0 в проекте через Registry (в REST API также нужно добавить через параметр Custom Swagger Definition Path); указание через Registry всех схем XSD или JSONSchems, которые используются для валидации запросов или трансформации; возможность получить документацию OpenAPI через запрос вида http://host:port/api_name?swagger.json; возможность получить WSDL через запрос вида http://host:port/api_name?wsdl, если публикуется сервис в формате SOAP. Надеемся, в этом списке вы найдете для себя что-нибудь полезное. В следующей статье расскажем о разработке SOAP-сервисов — там есть свои нюансы. Если есть вопросы по этому посту, будем рады ответить в комментариях. Кстати, 9 декабря  мы планируем провести онлайн-митап по IT-архитектуре. Дмитрий Зыков из Росбанка расскажет об автоматизации процессов управления архитектурой. Затем Дмитрий Бардин из Croc Code — об оценка модернизации инфраструктуры. И завершит основную программу Егор Слесаренко из Leroy Merlin с рассказом о том, как в его компании внедряют Composable Architecture. Начало митапа в 19:00, программа рассчитана часа на полтора, зарегистрироваться можно на сайте. Ждем вас! IT Architect, WSO2, API Management, IBM WebShpere