Verwendung von SOAP-to-REST zur Vereinfachung von Migration und Integration

Jinhua Luo

March 31, 2023

Technology

1. Was ist ein Web Service?

Ein Web Service wird vom World Wide Web Consortium (W3C) als ein Softwaresystem definiert, das darauf ausgelegt ist, interoperable Maschine-zu-Maschine-Interaktion über ein Netzwerk zu unterstützen.

Ein Web Service führt spezifische Aufgaben oder eine Reihe von Aufgaben aus und wird durch eine standardisierte XML-Darstellung eines Dienstes beschrieben, die als Web Services Description Language (WSDL) bezeichnet wird. Die Dienstbeschreibung liefert alle notwendigen Details für die Interaktion mit dem Dienst, einschließlich Nachrichtenformaten (die verwendet werden, um Operationen detailliert zu beschreiben), Transportprotokollen und dem Standort.

Andere Systeme würden mit dem Web Service über SOAP-Nachrichten interagieren, normalerweise unter Verwendung von HTTP in Verbindung mit XML-Serialisierung und anderen webbezogenen Standards.

Das Architekturdiagramm eines Web Services (beachten Sie, dass der Service Broker optional ist) sieht wie folgt aus:

Web Services Architektur

*Bildquelle (lizenziert unter CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_service

Die WSDL-Schnittstelle verbirgt detaillierte Informationen darüber, wie der Dienst implementiert ist, sodass die Nutzung des Dienstes unabhängig von der Hardware- oder Softwareplattform ist, die den Dienst implementiert, sowie von der Programmiersprache, die zur Erstellung des Dienstes verwendet wurde.

Anwendungen, die auf Web Services basieren, sind lose gekoppelt, komponentenorientiert und technologieübergreifend implementiert. Web Services können einzeln oder in Kombination mit anderen Web Services verwendet werden, um komplexe Aggregationen oder Geschäftstransaktionen durchzuführen.

Web Services sind die Implementierungseinheiten der Serviceorientierten Architektur (SOA), einer Entwurfsmethode, die verwendet wird, um monolithische Systeme zu ersetzen. Ein umfangreiches System kann in mehrere Web Services aufgeteilt werden, die dann zu einer großen Blackbox kombiniert werden können, um Geschäftslogik nach außen bereitzustellen. Die beliebten containerbasierten Microservices sind die neueste Ersetzung für Web Services. Viele Legacy-Systeme basieren jedoch bereits auf Web Services für die Implementierung und den Betrieb, sodass die Kompatibilität mit diesen Systemen trotz der rasanten technologischen Entwicklung eine Herausforderung bleibt.

WSDL (Web Services Description Language)

WSDL ist eine XML-Notation, die verwendet wird, um Web Services zu beschreiben. Die WSDL-Definition weist Clients an, wie sie Web Service-Anfragen schreiben sollen, und definiert die Schnittstelle, die vom Web Service-Anbieter bereitgestellt wird.

Die WSDL-Definition ist in mehrere Teile unterteilt, die das logische Interface des Web Services und die physischen Details spezifizieren. Physische Details umfassen Endpunktinformationen, wie z.B. die HTTP-Portnummer, sowie Bindungsinformationen, die festlegen, wie der SOAP-Nachrichteninhalt dargestellt wird und welches Transportverfahren verwendet werden soll.

Darstellung der Konzepte, die durch WSDL 1.1- und WSDL 2.0-Dokumente definiert werden.

Bildquelle (lizenziert unter CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_Services_Description_Language

  • Eine WSDL-Datei kann mehrere Dienste enthalten.
  • Ein Dienst kann mehrere Ports enthalten.
  • Ein Port definiert eine URL-Adresse (die für jeden Port variieren kann) und kann mehrere Operationen enthalten.
  • Jede Operation umfasst einen Eingabetyp und einen Ausgabetyp.
  • Typen definieren die Nachrichtenstruktur, einschließlich der Felder, die die Nachricht zusammensetzen, den Datentyp jedes Feldes (der verschachtelt sein kann) und die Anzahl der zulässigen Felder.

1.1 Was ist SOAP?

SOAP ist ein XML-Nachrichtenformat, das in Web Service-Interaktionen verwendet wird. SOAP-Nachrichten werden normalerweise über HTTP oder JMS gesendet, aber auch andere Transportprotokolle können verwendet werden. Die WSDL-Definition beschreibt die SOAP-Nutzung in einem bestimmten Web Service.

Es gibt zwei häufig verwendete Versionen von SOAP: SOAP 1.1 und SOAP 1.2.

SOAP-Struktur

Bildquelle (lizenziert unter CC 3.0 BY-SA): https://en.wikipedia.org/wiki/SOAP

Eine SOAP-Nachricht enthält die folgenden Teile:

  • Header-Metadaten, die normalerweise leer sind
  • Body
    • Der in WSDL definierte Nachrichtentyp
    • Neben erfolgreichen Antworten gibt es auch strukturierte Fehlermeldungen für Antworttypen.

Beispiel:

<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 Was ist REST?

Ein Web Service ist ein abstraktes Konzept, das auf jede Weise implementiert werden kann; REST ist beispielsweise eine beliebte Implementierungsmethode.

REST, kurz für Representational State Transfer, bedeutet wörtlich die Zustandsübertragung der Präsentationsschicht. Roy Thomas Fielding prägte den Begriff REST in seiner Doktorarbeit im Jahr 2000. Zu dieser Zeit boomte das Internet, und es bestand Bedarf an einer praktischen Definition für die Softwareentwicklung und die Interaktion mit Netzwerken.

Für eine lange Zeit konzentrierte sich die Softwareforschung hauptsächlich auf die Klassifizierung von Software-Designs und die Entwicklung von Designmethoden. Es wurde selten objektiv bewertet, wie sich verschiedene Designentscheidungen auf das Systemverhalten auswirken. Auf der anderen Seite konzentrierte sich die Netzwerkforschung hauptsächlich auf die Details des Kommunikationsverhaltens zwischen Systemen und darauf, wie die Leistung spezifischer Kommunikationsmechanismen verbessert werden kann, wobei oft übersehen wurde, dass die Änderung des Interaktionsstils einer Anwendung einen größeren Einfluss auf die Gesamtleistung hat als die Änderung des Interaktionsprotokolls. Mein Artikel zielt darauf ab, das Architekturdesign von netzwerkbasierten Anwendungssoftware zu verstehen und zu bewerten, die funktional ist, gut performt und für die Kommunikation geeignet ist, während sie den Architekturprinzipien entspricht.

Der Zugriff auf eine Website stellt einen Interaktionsprozess zwischen dem Client und dem Server dar. Während dieses Prozesses müssen sich Daten und Zustände ändern. Das HTTP-Protokoll ist zustandslos, was bedeutet, dass alle Zustände auf der Serverseite gespeichert sind. Daher muss der Client, wenn er den Server steuern möchte, ein Mittel verwenden, um eine "Zustandsübertragung" auf der Serverseite zu verursachen. Da diese Übertragung auf der Präsentationsschicht aufgebaut ist, wird sie als "Zustandsübertragung der Präsentationsschicht" bezeichnet.

Die vier grundlegenden Prinzipien von REST sind:

  1. Verwenden Sie HTTP-Verben: GET, POST, PUT, DELETE;
  2. Zustandslose Verbindung, der Server sollte nicht zu viel Kontextzustand speichern; das heißt, jede Anfrage ist unabhängig;
  3. Setzen Sie eine URI für jede Ressource;
  4. Verwenden Sie x-www-form-urlencoded oder JSON als Datenformat;

Die Umwandlung von SOAP in REST kann es Benutzern erleichtern, auf traditionelle Web Services in einer RESTful-Weise zuzugreifen, wodurch die Entwicklungskosten von SOAP-Clients reduziert werden. Darüber hinaus, wenn es dynamisch an jeden Web Service ohne Code-Entwicklung angepasst werden kann, wäre es noch perfekter.

Der größte Vorteil von REST ist, dass es kein Schema hat, was die Entwicklung erleichtert, und JSON hat eine höhere Lesbarkeit und geringere Redundanz.

2. Traditionelle Implementierungen von SOAP-zu-REST-Proxies

2.1 Manuelle Template-Konvertierung

Dieser Ansatz erfordert die Bereitstellung von Konvertierungsvorlagen für die Anfrage und die Antwort für jede Operation des Web Services, was auch von vielen anderen Gateway-Produkten verwendet wird.

Wir können das APISIX Body Transformer Plugin verwenden, um einen einfachen SOAP-zu-REST-Proxy zu erstellen und diesen Ansatz zu testen.

Beispielsweise konstruieren wir eine XML-formatierte Anfragevorlage für die getCountry-Operation des CountriesPortService in der WSDL-Datei basierend auf der Typdefinition.

Hier füllen wir das Namensfeld im JSON in das Namensfeld in 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
)

Eine XML-zu-JSON-Vorlage muss für die Antwort bereitgestellt werden, was etwas komplexer sein kann (insbesondere wenn SOAP-Versionsunterschiede berücksichtigt werden müssen). Dies liegt daran, dass festgestellt werden muss, ob die Antwort erfolgreich war:

  • Bei einer erfolgreichen Antwort können die Felder direkt auf JSON abgebildet werden.
  • Bei einer fehlgeschlagenen Antwort (d.h. einem Fehler) ist eine separate JSON-Struktur erforderlich, und es muss festgestellt werden, ob bestimmte optionale Felder vorhanden sind.
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
)

Konfigurieren Sie das APISIX-Routing und führen Sie Tests durch:

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"
}
# Fehlerantwort
{
  "message": "Your name is required.",
  "code": "SOAP-ENV:Server"
}

Wie wir sehen können, erfordert diese Methode ein manuelles Verständnis der Definition jeder Operation in der WSDL-Datei und der Web Service-Adresse, die jeder Operation entspricht. Wenn die WSDL-Datei groß ist, viele Operationen enthält und komplexe verschachtelte Typdefinitionen aufweist, kann dieser Ansatz sehr mühsam, schwer zu debuggen und fehleranfällig sein.

2.2 Apache Camel

https://camel.apache.org/

Camel ist ein bekanntes Java-Integrationsframework, das zur Implementierung von Routing-Pipelines verwendet wird, die verschiedene Protokolle und Geschäftslogik konvertieren, und SOAP-zu-REST ist nur einer seiner Anwendungsfälle.

Die Verwendung von Camel erfordert das Herunterladen und Importieren der WSDL-Datei, das Generieren von Stub-Code für den SOAP-Client und das Schreiben von Java-Code:

  • Definieren von REST-Endpunkten
  • Definieren von Protokollkonvertierungsrouten, z.B. wie JSON-Felder auf SOAP-Felder abgebildet werden

Als Beispiel betrachten wir einen Web Service zur Umrechnung von Temperatureinheiten:

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

  1. Generieren Sie SOAP-Client-Code basierend auf der WSDL-Datei mit Maven.

cxf-codegen-plugin generiert SOAP-Client-Endpunkte für uns, um auf den Web Service zuzugreifen.

<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. Schreiben des SOAP-Client-Beans.

Beachten Sie, dass wir den Namen des Beans als cxfConvertTemp speichern, der später bei der Definition der Camel-Routen verwendet wird.

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. Schreiben Sie zuerst die nachgelagerte REST-Route.

Aus dieser Route können wir sehen, dass sie RESTFul-URLs und deren Parameterdefinitionen definiert und die nächste Hop-Route für jede URL festlegt. Zum Beispiel nimmt /convert/celsius/to/fahrenheit/{num} den letzten Teil der URL als Parameter (vom Typ double) und stellt ihn der nächsten Hop-Route direct:celsius-to-fahrenheit zur Verfügung.

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. Schließlich schreiben Sie die vorgelagerte SOAP-Route und die Transformation zwischen vorgelagert und nachgelagert.
from("direct:celsius-to-fahrenheit")
    .removeHeaders("CamelHttp*")
    .process(new Processor() {
        @Override
        public void process(Exchange exchange) throws Exception {
            // Initialisieren Sie die SOAP-Anfrage
            // Füllen Sie den nachgelagerten Parameter num in den Body, der ein einfacher double-Typ ist.
            CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
            c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
            exchange.getIn().setBody(c);
        }
    })
    // Geben Sie die SOAP-Operation und den Namespace an
    // Definiert in der application.properties-Datei
    .setHeader(CxfConstants.OPERATION_NAME, constant("{{endpoint.operation.celsius.to.fahrenheit}}"))
    .setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
    // Senden Sie das Paket mit dem SOAP-Client-Bean, das von WSDL generiert wurde.
    .to("cxf:bean:cxfConvertTemp")
    .process(new Processor() {
        @Override
        public void process(Exchange exchange) throws Exception {
            // Verarbeiten Sie die SOAP-Antwort
            // Füllen Sie den Body, der ein double-Wert ist, in den String
            // Geben Sie den String an die nachgelagerte Route zurück
            MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
            CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
            exchange.getIn().setBody("Temp in Farenheit: " + r.getTemperatureInFahrenheit());
        }
    })
    .to("mock:output");
  1. Test
curl localhost:9090/convert/celsius/to/fahrenheit/50
Temp in Farenheit: 122.0

Wie wir sehen können, erfordert die Verwendung von Camel für SOAP-zu-REST die Definition von Routen und Konvertierungslogik für alle Operationen mit Java-Code, was Entwicklungskosten verursacht.

Ebenso kann die Verwendung von Camel als Proxy schmerzhaft sein, wenn die WSDL viele Dienste und Operationen enthält.

2.3 Fazit

Fassen wir die Nachteile traditioneller Methoden zusammen.

ModulCamel
WSDLmanuelle AnalyseCode-Generierung via Maven
vorgelagertmanuelle Analyseautomatische Konvertierung
Body definierenBereitstellung von Vorlagen für die Beurteilung und KonvertierungSchreiben von Konvertierungscode
Parameter erhaltenNginx-VariablenDefinition im Code oder Aufruf der SOAP-Client-Schnittstelle zur Beschaffung

Beide Ansätze haben Entwicklungskosten, und für jeden neuen Web Service müssen diese Entwicklungskosten wiederholt werden.

Die Entwicklungskosten sind proportional zur Komplexität des Web Services.

3. APISIX's SOAP-zu-REST-Proxy

Traditionelle Proxy-Ansätze erfordern entweder die Bereitstellung von Konvertierungsvorlagen oder das Schreiben von Konvertierungscode, was beides eine tiefgehende Analyse der WSDL-Dateien durch den Benutzer erfordert und erhebliche Entwicklungskosten verursacht.

APISIX bietet einen automatisierten Ansatz, der WSDL-Dateien automatisch analysiert und Konvertierungslogik für jede Operation bereitstellt, wodurch Entwicklungskosten für Benutzer entfallen.

Dies ist ein Enterprise-Plugin, kontaktieren Sie uns für weitere Informationen.

APISIX SOAP-zu-REST-Proxy

3.1 Codefreie automatische Konvertierung

Verwendung des APISIX SOAP-Proxys:

  • Keine manuelle Analyse oder Import von WSDL-Dateien erforderlich
  • Keine Notwendigkeit, Konvertierungsvorlagen zu definieren
  • Keine Notwendigkeit, irgendeinen Konvertierungs- oder Kopplungscode zu schreiben.

Benutzer müssen nur die URL der WSDL konfigurieren, und APISIX führt die Konvertierung automatisch durch. Es ist für jeden Web Service geeignet, ist ein generisches Programm und erfordert keine sekundäre Entwicklung für spezifische Anforderungen.

3.2 Dynamische Konfiguration

  • Die WSDL-URL kann an jede Route gebunden werden und kann wie andere APISIX-Ressourcenobjekte zur Laufzeit aktualisiert werden. Konfigurationsänderungen werden dynamisch übernommen, ohne dass APISIX neu gestartet werden muss.
  • Die in der WSDL-Datei enthaltenen Service-URL(s) (die mehrere URLs haben können), d.h. die vorgelagerte Adresse, werden automatisch erkannt und als SOAP-Upstream verwendet, ohne dass Benutzer sie analysieren und konfigurieren müssen.

3.3 Implementierungsmechanismus

  • Erhalten Sie den Inhalt der WSDL-Datei von der WSDL-URL und generieren Sie automatisch ein Proxy-Objekt nach der Analyse.
  • Das Proxy-Objekt ist für die Protokollkonvertierung verantwortlich:
    • Generieren Sie konforme SOAP-XML-Anfragen basierend auf JSON-Eingaben.
    • Konvertieren Sie SOAP-XML-Antworten in JSON-Antworten.
    • Greifen Sie auf den Web Service zu und behandeln Sie automatisch SOAP-Protokolldetails, wie z.B. Fehlertyp-Antworten.
    • Unterstützung von SOAP1.1 und SOAP1.2 sowie mehrerer Erweiterungsfunktionen, wie z.B. WS-Addressing.

3.4 Beispielkonfiguration

Erklärung der Konfigurationsparameter für das SOAP-Plugin:

ParameterErforderlich?Beschreibung
wsdl_urlJaWSDL-URL, z.B. https://apps.learnwebservices.com/services/tempconverter?wsdl

Test:

# Konfigurieren Sie das APISIX-Routing mit dem SOAP-Plugin
# Beachten Sie, dass eine einzelne Route alle Operationen ausführen kann, wobei der Operationsname über URL-Parameter angegeben wird
# Dies zeigt auch die Vorteile des dynamischen Proxys, da keine manuelle Analyse jeder Operation in der WSDL-Datei erforderlich ist.
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"}'
# Aufruf erfolgreich
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"}
# Aufruf fehlgeschlagen
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. Fazit

In der heutigen Welt sind Web Services so weit fortgeschritten, dass viele Unternehmensbenutzer auf traditionelle SOAP-basierte Web Services angewiesen sind, um ihre Dienste bereitzustellen. Aufgrund historischer Gründe und Kostenerwägungen sind diese Dienste nicht immer für eine vollständige Umgestaltung in RESTful-Dienste geeignet. Daher besteht bei vielen Unternehmensbenutzern eine erhebliche Nachfrage nach SOAP-zu-REST.

Das von APISIX bereitgestellte SOAP-zu-REST-Plugin kann eine Code-freie Proxy-Funktionalität erreichen, kann dynamisch konfiguriert werden und erfordert keine sekundäre Entwicklung, was für die Null-Kosten-Migration und Integration von Unternehmensbenutzern von Vorteil ist.

Tags: