Utiliser SOAP-to-REST pour simplifier la migration et l'intégration

Jinhua Luo

March 31, 2023

Technology

1. Qu'est-ce qu'un service web ?

Un service web est défini par le World Wide Web Consortium (W3C) comme un système logiciel conçu pour supporter une interaction interopérable entre machines sur un réseau.

Un service web exécute des tâches spécifiques ou un ensemble de tâches et est décrit par une représentation XML standard d'un service appelée Web Services Description Language (WSDL). La description du service fournit tous les détails nécessaires pour interagir avec le service, y compris les formats de message (utilisés pour décrire les opérations en détail), les protocoles de transport et l'emplacement.

D'autres systèmes interagiraient avec le service web en utilisant des messages SOAP, généralement en utilisant HTTP conjointement avec la sérialisation XML et d'autres standards liés au web.

Le diagramme d'architecture d'un service web (notez que le courtier de service est optionnel) est :

architecture des services web

*Source de l'image (sous licence CC 3.0 BY-SA) : https://en.wikipedia.org/wiki/Web_service

L'interface WSDL masque les informations détaillées sur la manière dont le service est implémenté, de sorte que l'utilisation du service est indépendante de la plateforme matérielle ou logicielle qui implémente le service, ainsi que du langage de programmation utilisé pour écrire le service.

Les applications basées sur les services web sont faiblement couplées, orientées composants et implémentées de manière intertechnologique. Les services web peuvent être utilisés individuellement ou avec d'autres services web pour effectuer des agrégations complexes ou des transactions commerciales.

Les services web sont les unités d'implémentation de l'architecture orientée services (SOA), une méthode de conception utilisée pour remplacer les systèmes monolithiques. Un système étendu peut être décomposé en plusieurs services web, qui peuvent ensuite être combinés en une grande boîte noire pour fournir une logique métier externe. Les microservices populaires basés sur des conteneurs sont le dernier remplacement des services web. Cependant, de nombreux systèmes hérités sont déjà basés sur des services web pour leur implémentation et leur fonctionnement, donc la compatibilité avec ces systèmes reste un défi malgré l'évolution rapide de la technologie.

WSDL (Web Services Description Language)

WSDL est une notation XML utilisée pour décrire les services web. La définition WSDL indique aux clients comment écrire des requêtes de service web et définit l'interface fournie par le fournisseur de services web.

La définition WSDL est divisée en plusieurs parties, spécifiant l'interface logique du service web et les détails physiques. Les détails physiques incluent les informations sur les points de terminaison, telles que le numéro de port HTTP, ainsi que les informations de liaison qui spécifient comment représenter le contenu des messages SOAP et quelle méthode de transport utiliser.

Représentation des concepts définis par les documents WSDL 1.1 et WSDL 2.0.

Source de l'image (sous licence CC 3.0 BY-SA) : https://en.wikipedia.org/wiki/Web_Services_Description_Language

  • Un fichier WSDL peut contenir plusieurs services.
  • Un service peut contenir plusieurs ports.
  • Un port définit une adresse URL (qui peut varier pour chaque port) et peut contenir plusieurs opérations.
  • Chaque opération inclut un type d'entrée et un type de sortie.
  • Les types définissent la structure du message, y compris les champs qui composent le message, le type de données de chaque champ (qui peut être imbriqué) et le nombre de champs autorisés.

1.1 Qu'est-ce que SOAP ?

SOAP est un format de message XML utilisé dans les interactions de services web. Les messages SOAP sont généralement envoyés via HTTP ou JMS, mais d'autres protocoles de transport peuvent également être utilisés. La définition WSDL décrit l'utilisation de SOAP dans un service web spécifique.

Il existe deux versions couramment utilisées de SOAP : SOAP 1.1 et SOAP 1.2.

Structure de SOAP

Source de l'image (sous licence CC 3.0 BY-SA) : https://en.wikipedia.org/wiki/SOAP

Un message SOAP contient les parties suivantes :

  • Métadonnées d'en-tête, qui sont généralement vides
  • Corps
    • Le type de message défini dans WSDL
    • En plus des réponses réussies, il existe également des messages d'erreur structurés pour les types de réponse.

Par exemple :

<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 Qu'est-ce que REST ?

Un service web est un concept abstrait qui peut être implémenté de n'importe quelle manière ; par exemple, REST est une méthode d'implémentation populaire.

REST, abréviation de Representational State Transfer, signifie littéralement transfert d'état de la couche de présentation. Roy Thomas Fielding a proposé le terme REST dans sa thèse de doctorat en 2000. À cette époque, Internet était en plein essor, et il y avait un besoin de définition pratique pour le développement de logiciels et l'interaction avec les réseaux.

Pendant longtemps, la recherche en logiciel s'est principalement concentrée sur la classification de la conception logicielle et l'évolution des méthodes de conception. Elle évalue rarement de manière objective l'impact des différents choix de conception sur le comportement du système. D'autre part, la recherche sur les réseaux se concentre principalement sur les détails du comportement de communication entre les systèmes et sur la manière d'améliorer les performances de mécanismes de communication spécifiques, négligeant souvent que le changement de style d'interaction de l'application a un impact plus important sur les performances globales que le changement de protocole d'interaction. Mon article vise à comprendre et évaluer la conception architecturale des logiciels d'application basés sur le réseau qui sont fonctionnels, performants et adaptés à la communication tout en respectant les principes architecturaux.

Accéder à un site web représente un processus d'interaction entre le client et le serveur. Au cours de ce processus, il doit y avoir des changements dans les données et l'état. Le protocole HTTP est sans état, ce qui signifie que tous les états sont stockés côté serveur. Par conséquent, si le client veut opérer sur le serveur, il doit utiliser certains moyens pour provoquer un "transfert d'état" côté serveur. Comme ce transfert est construit sur la couche de présentation, on l'appelle "transfert d'état de la couche de présentation".

Les quatre principes de base de REST sont :

  1. Utiliser les verbes HTTP : GET, POST, PUT, DELETE ;
  2. Connexion sans état, le serveur ne doit pas stocker trop d'état contextuel ; c'est-à-dire que chaque requête est indépendante ;
  3. Définir un URI pour chaque ressource ;
  4. Utiliser x-www-form-urlencoded ou JSON comme format de données ;

Convertir SOAP en REST peut faciliter l'accès des utilisateurs aux services web traditionnels de manière RESTful, réduisant ainsi le coût de développement des clients SOAP. De plus, s'il peut s'adapter dynamiquement à n'importe quel service web sans développement de code, ce serait encore plus parfait.

Le plus grand avantage de REST est qu'il n'a pas de schéma, ce qui le rend pratique à développer, et JSON a une meilleure lisibilité et une redondance plus faible.

2. Implémentations traditionnelles du proxy SOAP-to-REST

2.1 Conversion manuelle de modèles

Cette approche nécessite de fournir des modèles de conversion pour la requête et la réponse pour chaque opération du service web, ce qui est également utilisé par de nombreux autres produits de passerelle.

Nous pouvons utiliser le plugin APISIX body transformer pour créer un simple proxy SOAP-to-REST et essayer cette approche.

Par exemple, nous construisons un modèle de requête formaté en XML pour l'opération getCountry du service CountriesPortService dans le fichier WSDL basé sur la définition de type.

Ici, nous remplissons le champ name dans le JSON dans le champ name dans 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
)

Un modèle XML-to-JSON doit être fourni pour la réponse, ce qui peut être un peu plus complexe (surtout si des différences de version SOAP doivent être prises en compte). Cela est dû au fait qu'il doit être déterminé si la réponse a réussi :

  • Pour une réponse réussie, les champs peuvent être directement mappés en JSON.
  • Pour une réponse échouée (c'est-à-dire une faute), une structure JSON séparée est nécessaire, et il doit être déterminé si des champs optionnels spécifiques sont présents.
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
)

Configurer le routage APISIX et effectuer des tests :

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

Comme nous pouvons le voir, cette méthode nécessite une compréhension manuelle de la définition de chaque opération dans le fichier WSDL et de l'adresse du service web correspondant à chaque opération. Si le fichier WSDL est volumineux, contient un grand nombre d'opérations et a des définitions de types imbriquées complexes, alors cette approche peut être très fastidieuse, difficile à déboguer et sujette à des erreurs.

2.2 Apache Camel

https://camel.apache.org/

Camel est un framework d'intégration Java bien connu utilisé pour implémenter des pipelines de routage qui convertissent différents protocoles et logiques métier, et SOAP-to-REST est juste un de ses cas d'utilisation.

Utiliser Camel nécessite de télécharger et d'importer le fichier WSDL, de générer du code stub pour le client SOAP, et d'écrire du code Java :

  • Définir les points de terminaison REST
  • Définir les routes de conversion de protocole, comme la manière dont les champs JSON sont mappés aux champs SOAP

Comme exemple, considérons un service web de conversion d'unités de température :

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

  1. Générer le code client SOAP basé sur le fichier WSDL en utilisant Maven.

cxf-codegen-plugin générera pour nous les points de terminaison client SOAP pour accéder au service web.

<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. Écrire le bean client SOAP.

Notez que nous nous souvenons du nom du bean comme cxfConvertTemp, qui sera utilisé lors de la définition des routes Camel plus tard.

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. Écrire d'abord la route REST en aval.

De cette route, nous pouvons voir qu'elle définit des URL de style RESTFul et leurs définitions de paramètres, et définit le prochain saut de route pour chaque URL. Par exemple, /convert/celsius/to/fahrenheit/{num} prend la dernière partie de l'URL comme paramètre (de type double) et le fournit à la prochaine route 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. Enfin, écrire la route SOAP en amont et la transformation entre l'amont et l'aval.
from("direct:celsius-to-fahrenheit")
    .removeHeaders("CamelHttp*")
    .process(new Processor() {
        @Override
        public void process(Exchange exchange) throws Exception {
            // Initialiser la requête SOAP
            // Remplir le paramètre en aval num dans le corps, qui est un simple type double.
            CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
            c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
            exchange.getIn().setBody(c);
        }
    })
    // Spécifier l'opération SOAP et l'espace de noms
    // Défini dans le fichier application.properties
    .setHeader(CxfConstants.OPERATION_NAME, constant("{{endpoint.operation.celsius.to.fahrenheit}}"))
    .setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
    // Envoyer le paquet en utilisant le bean client SOAP généré par WSDL.
    .to("cxf:bean:cxfConvertTemp")
    .process(new Processor() {
        @Override
        public void process(Exchange exchange) throws Exception {
            // Gérer la réponse SOAP
            // Remplir le corps, qui est une valeur de type double, dans la chaîne
            // Retourner la chaîne à l'aval
            MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
            CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
            exchange.getIn().setBody("Temp in Farenheit: " + r.getTemperatureInFahrenheit());
        }
    })
    .to("mock:output");
  1. Tester
curl localhost:9090/convert/celsius/to/fahrenheit/50
Temp in Farenheit: 122.0

Comme nous pouvons le voir, utiliser Camel pour SOAP-to-REST nécessite de définir des routes et une logique de conversion pour toutes les opérations en utilisant du code Java, ce qui entraîne des coûts de développement.

De même, si le WSDL contient de nombreux services et opérations, utiliser Camel pour le proxying peut également être pénible.

2.3 Conclusion

Résumons les inconvénients des méthodes traditionnelles.

ModuleCamel
WSDLanalyse manuellegénération de code via Maven
amontanalyse manuelleconversion automatique
définir le corpsfournir des modèles pour le jugement et la conversionécrire du code de conversion
obtenir des paramètresvariables Nginxdéfinir dans le code ou appeler l'interface du client SOAP pour obtenir

Ces deux approches ont des coûts de développement, et pour chaque nouveau service web, ce coût de développement doit être répété.

Le coût de développement est proportionnel à la complexité du service web.

3. Proxy SOAP-to-REST d'APISIX

Les approches de proxy traditionnelles nécessitent soit de fournir des modèles de conversion, soit d'écrire du code de conversion, ce qui oblige les utilisateurs à analyser en profondeur les fichiers WSDL et entraîne des coûts de développement importants.

APISIX fournit une approche automatisée qui analyse automatiquement les fichiers WSDL et fournit une logique de conversion pour chaque opération, éliminant les coûts de développement pour les utilisateurs.

Il s'agit d'un plugin Enterprise, contactez-nous pour plus d'informations.

Proxy SOAP-to-REST d'APISIX

3.1 Conversion automatique sans code

Utiliser le proxy SOAP d'APISIX :

  • Aucune analyse ou importation manuelle des fichiers WSDL n'est nécessaire
  • Aucun besoin de définir des modèles de conversion
  • Aucun besoin d'écrire du code de conversion ou de couplage.

Les utilisateurs n'ont qu'à configurer l'URL du WSDL, et APISIX effectuera automatiquement la conversion. Il est adapté à n'importe quel service web, est un programme générique et ne nécessite pas de développement secondaire pour des besoins spécifiques.

3.2 Configuration dynamique

  • L'URL du WSDL peut être liée à n'importe quelle route et, comme d'autres objets de ressource APISIX, peut être mise à jour à l'exécution. Les changements de configuration prennent effet dynamiquement sans nécessiter de redémarrage d'APISIX.
  • L'URL du service contenu dans le fichier WSDL (qui peut avoir plusieurs URL), c'est-à-dire l'adresse en amont, sera automatiquement reconnue et utilisée comme l'amont SOAP sans que les utilisateurs aient besoin de l'analyser et de la configurer.

3.3 Mécanisme d'implémentation

  • Obtenir le contenu du fichier WSDL à partir de l'URL du WSDL et générer automatiquement un objet proxy après analyse.
  • L'objet proxy est responsable de la conversion de protocole :
    • Générer des requêtes XML SOAP conformes basées sur l'entrée JSON.
    • Convertir les réponses XML SOAP en réponses JSON.
    • Accéder au service web et gérer automatiquement les détails du protocole SOAP, tels que les réponses de type faute.
    • Prendre en charge SOAP1.1 et SOAP1.2 et plusieurs fonctionnalités d'extension, telles que WS-Addressing.

3.4 Exemple de configuration

Explication des paramètres de configuration pour le plugin SOAP :

ParamètreRequis ?Description
wsdl_urlOuiURL du WSDL, par exemple, https://apps.learnwebservices.com/services/tempconverter?wsdl

Test :

# Configurer le routage APISIX en utilisant le plugin SOAP
# Notez qu'une seule route peut exécuter toutes les opérations, avec le nom de l'opération spécifié à l'aide de paramètres d'URL
# Cela reflète également les avantages du proxy dynamique, éliminant le besoin d'analyse manuelle de chaque opération dans le fichier 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"}'
# Appel réussi
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"}
# Appel échoué
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. Conclusion

Dans le monde d'aujourd'hui, les services web ont évolué au point où de nombreux utilisateurs d'entreprise s'appuient sur des services web traditionnels basés sur SOAP pour fournir leurs services. Pour des raisons historiques et de coût, ces services ne sont pas toujours adaptés à une refonte complète en services RESTful. Par conséquent, il existe une demande importante pour SOAP-to-REST parmi de nombreux utilisateurs d'entreprise.

Le plugin SOAP-to-REST fourni par APISIX peut atteindre une fonctionnalité de proxy sans code, peut être configuré dynamiquement et ne nécessite pas de développement secondaire, ce qui est bénéfique pour la migration et l'intégration à coût zéro des activités des utilisateurs d'entreprise.

Tags: