Использование SOAP-to-REST для упрощения миграции и интеграции

Jinhua Luo

March 31, 2023

Technology

1. Что такое веб-сервис?

Веб-сервис определяется Консорциумом Всемирной паутины (W3C) как программная система, предназначенная для поддержки взаимодействия между машинами через сеть.

Веб-сервис выполняет определенные задачи или набор задач и описывается стандартным XML-представлением сервиса, называемым Web Services Description Language (WSDL). Описание сервиса предоставляет все необходимые детали для взаимодействия с сервисом, включая форматы сообщений (используемые для детального описания операций), транспортные протоколы и местоположение.

Другие системы взаимодействуют с веб-сервисом с помощью сообщений SOAP, обычно используя HTTP в сочетании с XML-сериализацией и другими веб-стандартами.

Архитектурная схема веб-сервиса (обратите внимание, что сервисный брокер является необязательным):

архитектура веб-сервисов

*Источник изображения (лицензия CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_service

Интерфейс WSDL скрывает детальную информацию о том, как реализован сервис, чтобы использование сервиса было независимым от аппаратного или программного обеспечения, на котором он реализован, а также от языка программирования, используемого для написания сервиса.

Приложения, основанные на веб-сервисах, являются слабо связанными, компонентно-ориентированными и кросс-технологичными реализациями. Веб-сервисы могут использоваться отдельно или вместе с другими веб-сервисами для выполнения сложных агрегаций или бизнес-транзакций.

Веб-сервисы являются единицами реализации Service-oriented architecture (SOA), метода проектирования, используемого для замены монолитных систем. Обширная система может быть разбита на несколько веб-сервисов, которые затем могут быть объединены в большой черный ящик для предоставления бизнес-логики внешним образом. Популярные микросервисы на основе контейнеров являются последней заменой веб-сервисов. Однако многие устаревшие системы уже основаны на веб-сервисах для реализации и работы, поэтому совместимость с этими системами остается проблемой, несмотря на быстрое развитие технологий.

WSDL (Web Services Description Language)

WSDL — это XML-нотация, используемая для описания веб-сервисов. Определение WSDL инструктирует клиентов о том, как писать запросы к веб-сервису, и определяет интерфейс, предоставляемый поставщиком веб-сервиса.

Определение WSDL разделено на несколько частей, указывающих логический интерфейс веб-сервиса и физические детали. Физические детали включают информацию о конечной точке, такую как номер порта HTTP, а также информацию о привязке, которая указывает, как представлять содержимое сообщения SOAP и какой транспортный метод использовать.

Представление концепций, определенных документами WSDL 1.1 и WSDL 2.0.

Источник изображения (лицензия CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_Services_Description_Language

  • Файл WSDL может содержать несколько сервисов.
  • Сервис может содержать несколько портов.
  • Порт определяет URL-адрес (который может варьироваться для каждого порта) и может содержать несколько операций.
  • Каждая операция включает тип ввода и тип вывода.
  • Типы определяют структуру сообщения, включая то, какие поля составляют сообщение, тип данных каждого поля (который может быть вложенным) и количество допустимых полей.

1.1 Что такое SOAP

SOAP — это формат XML-сообщений, используемый во взаимодействии веб-сервисов. Сообщения SOAP обычно отправляются через HTTP или JMS, но могут использоваться и другие транспортные протоколы. Определение WSDL описывает использование SOAP в конкретном веб-сервисе.

Существует две часто используемые версии SOAP: SOAP 1.1 и SOAP 1.2.

Структура SOAP

Источник изображения (лицензия CC 3.0 BY-SA): https://en.wikipedia.org/wiki/SOAP

Сообщение SOAP содержит следующие части:

  • Метаданные заголовка, которые обычно пусты
  • Тело
    • Тип сообщения, определенный в WSDL
    • Помимо успешных ответов, существуют также структурированные сообщения об ошибках для типов ответов.

Например:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header></SOAP-ENV:Header> <SOAP-ENV:Body> <ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service"> <ns2:country> <ns2:name>Spain</ns2:name> <ns2:population>46704314</ns2:population> <ns2:capital>Madrid</ns2:capital> <ns2:currency>EUR</ns2:currency> </ns2:country> </ns2:getCountryResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

1.2 Что такое REST

Веб-сервис — это абстрактная концепция, которая может быть реализована любым способом; например, REST — это популярный метод реализации.

REST, сокращение от Representational State Transfer, буквально означает передачу состояния уровня представления. Рой Томас Филдинг предложил термин REST в своей докторской диссертации в 2000 году. В то время интернет активно развивался, и возникла необходимость в практическом определении разработки программного обеспечения и взаимодействия с сетями.

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

Доступ к веб-сайту представляет собой процесс взаимодействия между клиентом и сервером. В ходе этого процесса обязательно происходят изменения данных и состояния. Протокол HTTP является безсостоятельным, что означает, что все состояния хранятся на стороне сервера. Поэтому, если клиент хочет управлять сервером, он должен использовать некоторые средства для вызова "передачи состояния" на стороне сервера. Поскольку эта передача построена на уровне представления, она называется "передачей состояния уровня представления".

Четыре основных принципа REST:

  1. Использование HTTP-глаголов: GET, POST, PUT, DELETE;
  2. Безсостоятельное соединение, сервер не должен хранить слишком много контекстного состояния; то есть каждый запрос независим;
  3. Установка URI для каждого ресурса;
  4. Использование x-www-form-urlencoded или JSON в качестве формата данных;

Преобразование SOAP в REST может облегчить пользователям доступ к традиционным веб-сервисам в RESTful-стиле, снижая затраты на разработку клиентов SOAP. Более того, если можно динамически адаптироваться к любому веб-сервису без написания кода, это было бы еще более идеально.

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

2. Традиционные реализации прокси SOAP-to-REST

2.1 Ручное преобразование шаблонов

Этот подход требует предоставления шаблонов преобразования для запроса и ответа для каждой операции веб-сервиса, что также используется многими другими продуктами шлюзов.

Мы можем использовать плагин APISIX body transformer для создания простого прокси SOAP-to-REST и попробовать этот подход.

Например, мы создаем XML-форматированный шаблон запроса для операции getCountry сервиса CountriesPortService в файле WSDL на основе определения типа.

Здесь мы заполняем поле name в JSON в поле name в getCountryRequest.

req_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n' <?xml version="1.0"?> <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Body> <ns0:getCountryRequest xmlns:ns0="http://spring.io/guides/gs-producing-web-service"> <ns0:name>{{_escape_xml(name)}}</ns0:name> </ns0:getCountryRequest> </soap-env:Body> </soap-env:Envelope> EOF )

Для ответа необходимо предоставить шаблон XML-to-JSON, который может быть немного сложнее (особенно если нужно учитывать различия версий SOAP). Это связано с тем, что необходимо определить, был ли ответ успешным:

  • Для успешного ответа поля могут быть напрямую отображены в JSON.
  • Для неудачного ответа (т.е. ошибки) требуется отдельная структура JSON, и необходимо определить, присутствуют ли определенные опциональные поля.
rsp_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n' {% if Envelope.Body.Fault == nil then %} { "currency":"{{Envelope.Body.getCountryResponse.country.currency}}", "population":{{Envelope.Body.getCountryResponse.country.population}}, "capital":"{{Envelope.Body.getCountryResponse.country.capital}}", "name":"{{Envelope.Body.getCountryResponse.country.name}}" } {% else %} { "message":{*_escape_json(Envelope.Body.Fault.faultstring[1])*}, "code":"{{Envelope.Body.Fault.faultcode}}" {% if Envelope.Body.Fault.faultactor ~= nil then %} , "actor":"{{Envelope.Body.Fault.faultactor}}" {% end %} } {% end %} EOF )

Настройка маршрута APISIX и выполнение теста:

curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: xxx' -X PUT -d ' { "methods": ["POST"], "uri": "/ws/getCountry", "plugins": { "body-transformer": { "request": { "template": "'"$req_template"'" }, "response": { "template": "'"$rsp_template"'" } } }, "upstream": { "type": "roundrobin", "nodes": { "localhost:8080": 1 } } }' curl -s http://127.0.0.1:9080/ws/getCountry \ -H 'content-type: application/json' \ -X POST -d '{"name": "Spain"}' | jq { "currency": "EUR", "population": 46704314, "capital": "Madrid", "name": "Spain" } # Ответ с ошибкой { "message": "Your name is required.", "code": "SOAP-ENV:Server" }

Как мы видим, этот метод требует ручного понимания определения каждой операции в файле WSDL и адреса веб-сервиса, соответствующего каждой операции. Если файл WSDL большой, содержит множество операций и имеет сложные вложенные определения типов, то этот подход может быть очень трудоемким, сложным для отладки и подверженным ошибкам.

2.2 Apache Camel

https://camel.apache.org/

Camel — это известный Java-фреймворк для интеграции, используемый для реализации маршрутных конвейеров, которые преобразуют различные протоколы и бизнес-логику, и SOAP-to-REST — это лишь один из его случаев использования.

Использование Camel требует загрузки и импорта файла WSDL, генерации заглушек для клиента SOAP и написания Java-кода:

  • Определение конечных точек REST
  • Определение маршрутов преобразования протоколов, например, как поля JSON отображаются на поля SOAP

В качестве примера рассмотрим веб-сервис для преобразования единиц температуры:

https://apps.learnwebservices.com/services/tempconverter?wsdl

  1. Генерация кода клиента SOAP на основе файла WSDL с использованием Maven.

cxf-codegen-plugin сгенерирует конечные точки клиента SOAP для доступа к веб-сервису.

<build> <plugins> <plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <wsdlOptions> <wsdlOption> <wsdl>src/main/resources/TempConverter.wsdl</wsdl> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
  1. Написание бина клиента SOAP.

Обратите внимание, что мы запоминаем имя бина как cxfConvertTemp, которое будет использоваться при определении маршрутов Camel позже.

import com.learnwebservices.services.tempconverter.TempConverterEndpoint; @Configuration public class CxfBeans { @Value("${endpoint.wsdl}") private String SOAP_URL; @Bean(name = "cxfConvertTemp") public CxfEndpoint buildCxfEndpoint() { CxfEndpoint cxf = new CxfEndpoint(); cxf.setAddress(SOAP_URL); cxf.setServiceClass(TempConverterEndpoint.class); return cxf; } }
  1. Сначала напишем маршрут REST.

Из этого маршрута видно, что он определяет URL-адреса в стиле RESTFul и их параметры, а также определяет следующий маршрут для каждого URL-адреса. Например, /convert/celsius/to/fahrenheit/{num} принимает последнюю часть URL-адреса как параметр (типа double) и предоставляет его следующему маршруту direct:celsius-to-fahrenheit.

rest("/convert") .get("/celsius/to/fahrenheit/{num}") .consumes("text/plain").produces("text/plain") .description("Convert a temperature in Celsius to Fahrenheit") .param().name("num").type(RestParamType.path).description("Temperature in Celsius").dataType("int").endParam() .to("direct:celsius-to-fahrenheit");
  1. Наконец, напишем маршрут SOAP и преобразование между маршрутами.
from("direct:celsius-to-fahrenheit") .removeHeaders("CamelHttp*") .process(new Processor() { @Override public void process(Exchange exchange) throws Exception { // Инициализация запроса SOAP // Заполнение параметра num из маршрута в тело, который является простым типом double. CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest(); c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString())); exchange.getIn().setBody(c); } }) // Указание операции SOAP и пространства имен // Определено в файле application.properties .setHeader(CxfConstants.OPERATION_NAME, constant("{{endpoint.operation.celsius.to.fahrenheit}}")) .setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}")) // Отправка пакета с использованием бина клиента SOAP, сгенерированного WSDL. .to("cxf:bean:cxfConvertTemp") .process(new Processor() { @Override public void process(Exchange exchange) throws Exception { // Обработка ответа SOAP // Заполнение тела, которое является значением типа double, в строку // Возврат строки в маршрут MessageContentsList response = (MessageContentsList) exchange.getIn().getBody(); CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0); exchange.getIn().setBody("Temp in Farenheit: " + r.getTemperatureInFahrenheit()); } }) .to("mock:output");
  1. Тест
curl localhost:9090/convert/celsius/to/fahrenheit/50 Temp in Farenheit: 122.0

Как мы видим, использование Camel для SOAP-to-REST требует определения маршрутов и логики преобразования для всех операций с использованием Java-кода, что влечет за собой затраты на разработку.

Аналогично, если WSDL содержит множество сервисов и операций, использование Camel для проксирования также может быть болезненным.

2.3 Заключение

Давайте подведем итоги недостатков традиционных методов.

МодульCamel
WSDLручной анализгенерация кода через Maven
маршрутручной анализавтоматическое преобразование
определение телапредоставление шаблонов для проверки и преобразованиянаписание кода преобразования
получение параметровпеременные Nginxопределение в коде или вызов интерфейса клиента SOAP для получения

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

Затраты на разработку пропорциональны сложности веб-сервиса.

3. Прокси SOAP-to-REST в APISIX

Традиционные подходы к проксированию требуют либо предоставления шаблонов преобразования, либо написания кода преобразования, что требует от пользователей глубокого анализа файлов WSDL и влечет значительные затраты на разработку.

APISIX предоставляет автоматизированный подход, который автоматически анализирует файлы WSDL и предоставляет логику преобразования для каждой операции, устраняя затраты на разработку для пользователей.

Это плагин Enterprise, свяжитесь с нами для получения дополнительной информации.

Прокси SOAP-to-REST в APISIX

3.1 Автоматическое преобразование без написания кода

Использование прокси SOAP в APISIX:

  • Не требуется ручной анализ или импорт файлов WSDL
  • Не нужно определять шаблоны преобразования
  • Не нужно писать код преобразования или связывания.

Пользователям нужно только настроить URL WSDL, и APISIX автоматически выполнит преобразование. Это подходит для любого веб-сервиса, является универсальным решением и не требует дополнительной разработки для конкретных нужд.

3.2 Динамическая конфигурация

  • URL WSDL может быть привязан к любому маршруту и, как и другие объекты ресурсов APISIX, может быть обновлен во время выполнения. Изменения конфигурации вступают в силу динамически без необходимости перезапуска APISIX.
  • URL сервиса(ов), содержащийся в файле WSDL (который может иметь несколько URL), т.е. адрес вышестоящего сервера, будет автоматически распознан и использован как вышестоящий сервер SOAP без необходимости анализа и настройки пользователем.

3.3 Механизм реализации

  • Получение содержимого файла WSDL из URL WSDL и автоматическая генерация прокси-объекта после анализа.
  • Прокси-объект отвечает за преобразование протоколов:
    • Генерация соответствующих XML-запросов SOAP на основе JSON-ввода.
    • Преобразование XML-ответов SOAP в JSON-ответы.
    • Доступ к веб-сервису и автоматическая обработка деталей протокола SOAP, таких как ответы с ошибками.
    • Поддержка SOAP1.1 и SOAP1.2 и нескольких расширенных функций, таких как WS-Addressing.

3.4 Пример конфигурации

Описание параметров конфигурации для плагина SOAP:

ПараметрОбязателен?Описание
wsdl_urlДаURL WSDL, например, https://apps.learnwebservices.com/services/tempconverter?wsdl

Тест:

# Настройка маршрута APISIX с использованием плагина SOAP # Обратите внимание, что один маршрут может выполнять все операции, с указанием имени операции с помощью параметров URL # Это также отражает преимущества динамического прокси, устраняя необходимость ручного анализа каждой операции в файле WSDL. curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: xxx' -X PUT -d ' { "methods": ["POST"], "uri": "/getCountry", "plugins": { "soap": { "wsdl_url": "http://localhost:8080/ws/countries.wsdl" } } }' curl 'http://127.0.0.1:9080/getCountry' \ -X POST -d '{"name": "Spain"}' # Успешный вызов HTTP/1.1 200 OK Date: Tue, 06 Dec 2022 08:07:48 GMT Content-Type: text/plain; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Server: APISIX/2.99.0 {"currency":"EUR","population":46704314,"capital":"Madrid","name":"Spain"} # Неудачный вызов HTTP/1.1 502 Bad Gateway Date: Tue, 03 Jan 2023 13:43:33 GMT Content-Type: text/plain; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Server: APISIX/2.99.0 {"message":"Your name is required.","actor":null,"code":"SOAP-ENV:Server","subcodes":null,"detail":null}

4. Заключение

В современном мире веб-сервисы продвинулись до такой степени, что многие корпоративные пользователи полагаются на традиционные веб-сервисы на основе SOAP для предоставления своих услуг. По историческим причинам и из-за соображений стоимости эти сервисы не всегда подходят для полного рефакторинга в RESTful-сервисы. В результате существует значительный спрос на SOAP-to-REST среди многих корпоративных пользователей.

Плагин SOAP-to-REST, предоставляемый APISIX, может достичь функциональности прокси без написания кода, может быть динамически настроен и не требует дополнительной разработки, что полезно для нулевой стоимости миграции и интеграции бизнеса корпоративных пользователей.

Tags: