Remote - Client HTTP
Introduction
Souvent, dans nos applications, nous devons effectuer des requêtes HTTP vers des services externes. Cela peut être fait en utilisant la ressource _remote.
Pour effectuer des appels HTTP en interne vers d'autres services ou serveurs internes sur l'ordinateur, ou pour appeler un service sur Internet, l'utilisation de la ressource Remote est essentielle.
Voir la documentation de la ressource Remote.
Nous pouvons même envoyer et recevoir des données en JSON et téléverser ou télécharger des fichiers.
Initialisation
Pour pouvoir effectuer un appel HTTP, nous devons d'abord l'initialiser, pour cela nous utiliserons la méthode init de _remote et nous stockerons ensuite le résultat de cette opération dans une variable appelée remote afin de pouvoir référencer la requête :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
// Prend en charge l'envoi de paramètres HTTP
const remoteSimple = _remote.init();
// Prend en charge l'envoi de JSON :
const remoteJSON = _remote.init().asJSON();
// Prend en charge l'envoi de paramètres HTTP
const remoteSimple = _remote.init();
// Prend en charge l'envoi de JSON :
const remoteJSON = _remote.init().asJSON();
# Prend en charge l'envoi de paramètres HTTP
remoteSimple = _remote.init()
# Prend en charge l'envoi de JSON :
remoteJSON = _remote.init().asJSON()
# Prend en charge l'envoi de paramètres HTTP
remoteSimple = _remote.init()
# Prend en charge l'envoi de JSON :
remoteJSON = _remote.init().asJSON()
// Prend en charge l'envoi de paramètres HTTP
val remoteSimple = _remote.init()
// Prend en charge l'envoi de JSON :
val remoteJSON = _remote.init().asJSON()
// Prend en charge l'envoi de paramètres HTTP
final remoteSimple = _remote.init()
// Prend en charge l'envoi de JSON :
final remoteJSON = _remote.init().asJSON()
Configuration
Nous pouvons également définir des configurations globales dans l'application, remarquez cet exemple complet :
...
"remote": {
"exemple-basic-auth": {
"url": "https://exemple.com/api/rest/exemple",
"form": true,
"authorization": {
"username": "admin",
"password": "motdepasse"
},
"data": {
"param1": "valeur 1...",
"param2": "valeur 2..."
}
},
"exemple-auth-token": {
"urlPrefix": "https://exemple.com/api",
"json": true,
"authorization": "Bearer JETON-AUTH-ICI",
"connectTimeout": 5000,
"readTimeout": 60000,
}
},
...
Nous pouvons configurer un préfixe d'URL (urlPrefix) ou l'URL complète (url).
Nous pouvons définir si la requête par défaut doit envoyer les données sous forme de Form ou JSON.
Prend en charge l'envoi d'une autorisation de base avec identifiant et mot de passe ou jeton.
Et il est possible de définir les données par défaut qui seront envoyées (data), ainsi que les délais limites de connexion (connectTimeout) et de lecture (readTimeout).
Utilisation
Pour utiliser la ressource Remote avec les configurations définies dans l'application, il suffit d'initialiser la ressource avec la clé de configuration, par exemple :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const reponse = _remote.init("exemple-basic-auth").post();
const reponse = _remote.init("exemple-basic-auth").post();
reponse = _remote.init("exemple-basic-auth").post()
reponse = _remote.init("exemple-basic-auth").post()
val reponse = _remote.init("exemple-basic-auth").post()
final reponse = _remote.init("exemple-basic-auth").post()
Et si l'URL de préfixe est définie :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init("exemple-auth-token");
const reponse = remote.post(
"/services/charge-donnees",
_val.map()
.set("param1", "valeur 1")
.set("param2", "valeur 2")
);
const remote = _remote.init("exemple-auth-token");
const reponse = remote.post(
"/services/charge-donnees",
_val.map()
.set("param1", "valeur 1")
.set("param2", "valeur 2")
);
remote = _remote.init("exemple-auth-token")
reponse = remote.post(
"/services/charge-donnees",
_val.map()
.set("param1", "valeur 1")
.set("param2", "valeur 2")
)
remote = _remote.init("exemple-auth-token")
reponse = remote.post(
"/services/charge-donnees",
_val.map()
.set("param1", "valeur 1")
.set("param2", "valeur 2")
)
val remote = _remote.init("exemple-auth-token")
val reponse = remote.post(
"/services/charge-donnees",
_val.map()
.set("param1", "valeur 1")
.set("param2", "valeur 2")
)
final remote = _remote.init("exemple-auth-token")
final reponse = remote.post(
"/services/charge-donnees",
_val.map()
.set("param1", "valeur 1")
.set("param2", "valeur 2")
)
Requête HTTP
Voyez comment nous pouvons initier une requête HTTP en utilisant la ressource Remote.
Envoyer du JSON
Pour envoyer des données au format JSON, nous devons indiquer le type de contenu comme JSON :
La façon simple de le faire est d'utiliser la méthode asJSON(), par exemple :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
remote.setURL("http://exemple.com/enregistrer");
const reponse = remote.post(
_val.map()
.set("donnees", "exemple")
.set("json", true)
);
const remote = _remote.init().asJSON();
remote.setURL("http://exemple.com/enregistrer");
const reponse = remote.post(
_val.map()
.set("donnees", "exemple")
.set("json", true)
);
remote = _remote.init().asJSON()
remote.setURL("http://exemple.com/enregistrer")
reponse = remote.post(
_val.map()
.set("donnees", "exemple")
.set("json", true)
)
remote = _remote.init().asJSON()
remote.setURL("http://exemple.com/enregistrer")
reponse = remote.post(
_val.map()
.set("donnees", "exemple")
.set("json", true)
)
val remote = _remote.init().asJSON()
remote.setURL("http://exemple.com/enregistrer")
val reponse = remote.post(
_val.map()
.set("donnees", "exemple")
.set("json", true)
)
final remote = _remote.init().asJSON()
remote.setURL("http://exemple.com/enregistrer")
final reponse = remote.post(
_val.map()
.set("donnees", "exemple")
.set("json", true)
)
Ainsi, les données seront envoyées en JSON pour les méthodes HTTP POST et PUT, mais pour GET et DELETE, les données sont toujours envoyées dans la chaîne de requête car ces méthodes ne supportent pas, selon le protocole HTTP, un corps avec des données.
Méthodes HTTP
Nous n'avons pas besoin d'indiquer explicitement dans l'en-tête de l'appel sa méthode HTTP, pour cela notre référence d'appel possède une fonction propre pour chaque type de méthode HTTP :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init();
remote.setURL("http://exemple.com");
remote.get(); // pour les appels GET
remote.post(); // pour les appels POST
remote.put(); // pour les appels PUT
remote.delete(); // pour les appels DELETE
const remote = _remote.init();
remote.setURL("http://exemple.com");
remote.get(); // pour les appels GET
remote.post(); // pour les appels POST
remote.put(); // pour les appels PUT
remote.delete(); // pour les appels DELETE
remote = _remote.init()
remote.setURL("http://exemple.com")
remote.get() # pour les appels GET
remote.post() # pour les appels POST
remote.put() # pour les appels PUT
remote.delete() # pour les appels DELETE
remote = _remote.init()
remote.setURL("http://exemple.com")
remote.get() # pour les appels GET
remote.post() # pour les appels POST
remote.put() # pour les appels PUT
remote.delete() # pour les appels DELETE
val remote = _remote.init()
remote.setURL("http://exemple.com")
remote.get() // pour les appels GET
remote.post() // pour les appels POST
remote.put() // pour les appels PUT
remote.delete() // pour les appels DELETE
final remote = _remote.init()
remote.setURL("http://exemple.com")
remote.get() // pour les appels GET
remote.post() // pour les appels POST
remote.put() // pour les appels PUT
remote.delete() // pour les appels DELETE
En-tête HTTP
Pour configurer les paramètres de l'en-tête HTTP, nous pouvons définir les clés avec la méthode getHeader(), par exemple :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init();
remote.getHeader().set("CLE", "VALEUR");
const remote = _remote.init();
remote.getHeader().set("CLE", "VALEUR");
remote = _remote.init()
remote.getHeader().set("CLE", "VALEUR")
remote = _remote.init()
remote.getHeader().set("CLE", "VALEUR")
val remote = _remote.init()
remote.getHeader().set("CLE", "VALEUR")
final remote = _remote.init()
remote.getHeader().set("CLE", "VALEUR")
URL de la requête
Pour indiquer l'URL de la requête, il suffit de la passer comme premier paramètre des fonctions des méthodes HTTP respectives :
const remote = _remote.init().asJSON()
remote.get("http://exemple.com") // pour les appels GET
remote.post("http://exemple.com") // pour les appels POST
remote.put("http://exemple.com") // pour les appels PUT
remote.delete("http://exemple.com") // pour les appels DELETE
Ajout de données à la requête
Si vous devez ajouter des données au corps de la requête, il suffit de les passer comme deuxième paramètre de la fonction de la méthode respective :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
remote.get("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels GET
remote.post("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels POST
remote.put("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels PUT
remote.delete("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels DELETE
const remote = _remote.init().asJSON();
remote.get("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels GET
remote.post("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels POST
remote.put("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels PUT
remote.delete("http://exemple.com", _val.map().set("id_utilisateur", 1)); // pour les appels DELETE
remote = _remote.init().asJSON()
remote.get("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels GET
remote.post("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels POST
remote.put("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels PUT
remote.delete("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels DELETE
remote = _remote.init().asJSON()
remote.get("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels GET
remote.post("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels POST
remote.put("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels PUT
remote.delete("http://exemple.com", _val.map().set("id_utilisateur", 1)) # pour les appels DELETE
val remote = _remote.init().asJSON()
remote.get("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels GET
remote.post("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels POST
remote.put("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels PUT
remote.delete("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels DELETE
final remote = _remote.init().asJSON()
remote.get("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels GET
remote.post("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels POST
remote.put("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels PUT
remote.delete("http://exemple.com", _val.map().set("id_utilisateur", 1)) // pour les appels DELETE
Réponse
Après avoir reçu la réponse de la requête, nous pouvons lire ses données en utilisant la méthode json ou content :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
const reponse = remote.post(
"http://exemple.com",
_val.map().set("id_utilisateur", 1)
);
if (reponse.ok()) {
_log.info("Réponse en JSON", reponse.json());
} else {
_log.info("Échec avec le statut : "+ reponse.statusCode(), reponse.content());
}
const remote = _remote.init().asJSON();
const reponse = remote.post(
"http://exemple.com",
_val.map().set("id_utilisateur", 1)
);
if (reponse.ok()) {
_log.info("Réponse en JSON", reponse.json());
} else {
_log.info("Échec avec le statut : "+ reponse.statusCode(), reponse.content());
}
remote = _remote.init().asJSON()
reponse = remote.post(
"http://exemple.com",
_val.map().set("id_utilisateur", 1)
)
if reponse.ok():
_log.info("Réponse en JSON", reponse.json())
else:
_log.info(
f"Échec avec le statut : {reponse.statusCode()}",
reponse.content()
)
remote = _remote.init().asJSON()
reponse = remote.post(
"http://exemple.com",
_val.map().set("id_utilisateur", 1)
)
if reponse.ok()
_log.info("Réponse en JSON", reponse.json())
else
_log.info(
"Échec avec le statut : #{reponse.statusCode()}",
reponse.content()
)
end
val remote = _remote.init().asJSON()
val reponse = remote.post(
"http://exemple.com",
_val.map().set("id_utilisateur", 1)
)
if (reponse.ok()) {
_log.info("Réponse en JSON", reponse.json())
} else {
_log.info(
"Échec avec le statut : ${reponse.statusCode()}",
reponse.content()
)
}
final remote = _remote.init().asJSON()
final reponse = remote.post(
"http://exemple.com",
_val.map().set("id_utilisateur", 1)
)
if (reponse.ok()) {
_log.info("Réponse en JSON", reponse.json())
} else {
_log.info("Échec avec le statut : "+ reponse.statusCode(), reponse.content())
}
Dans la réponse, avec la fonction json(), nous obtenons le tableau ou l'objet fourni comme Values, et avec la fonction content()
nous obtenons le contenu en texte brut ou le HTML, et le numéro du code de statut reçu se trouve dans statusCode(), sachant que ok()
renvoie une valeur booléenne qui est vraie si le code de statut est dans l'intervalle de 200 à 299.
Téléchargement
Pour télécharger des fichiers, nous utilisons le remote avec asBinary() pour indiquer qu'il doit s'agir d'un transfert binaire.
Voyez cet exemple de service complet :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const telechargement = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');
if (!telechargement.ok()) {
_log.error(`Statut du téléchargement du fichier : ${telechargement.statusCode()}\n${telechargement.toString()}`);
_header.status(500);
_out.json({resultat: false, erreur: 'echec-telechargement-fichier'});
_exec.stop();
}
const fichier = telechargement.file();
_log.info(`Nom du fichier téléchargé : ${fichier.name()}`);
fichier.save(_app.file(`storage/filesystem/server/${fichier.name()}`));
fichier.save(_storage.filesystem("server", "autre.png").file());
fichier.save(_os.file("/tmp/telechargement.png"));
_out.json({resultat: true});
const telechargement = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');
if (!telechargement.ok()) {
_log.error(`Statut du téléchargement du fichier : ${telechargement.statusCode()}\n${telechargement.toString()}`);
_header.status(500);
_out.json({resultat: false, erreur: 'echec-telechargement-fichier'});
_exec.stop();
}
const fichier = telechargement.file();
_log.info(`Nom du fichier téléchargé : ${fichier.name()}`);
fichier.save(_app.file(`storage/filesystem/server/${fichier.name()}`));
fichier.save(_storage.filesystem("server", "autre.png").file());
fichier.save(_os.file("/tmp/telechargement.png"));
_out.json({resultat: true});
telechargement = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if not telechargement.ok():
_log.error(f"Statut du téléchargement du fichier : {telechargement.statusCode()}\n{telechargement.toString()}")
_header.status(500)
_out.json(_val.map().set("resultat", False).set("erreur", "echec-telechargement-fichier"))
_exec.stop()
fichier = telechargement.file()
_log.info(f"Nom du fichier téléchargé : {fichier.name()}")
fichier.save(_app.file(f"storage/filesystem/server/{fichier.name()}"))
fichier.save(_storage.filesystem("server", "autre.png").file())
fichier.save(_os.file("/tmp/telechargement.png"))
_out.json(_val.map().set("resultat", True))
telechargement = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
unless telechargement.ok()
_log.error("Statut du téléchargement du fichier : #{telechargement.statusCode()}\n#{telechargement.toString()}")
_header.status(500)
_out.json(_val.map().set("resultat", false).set("erreur", "echec-telechargement-fichier"))
_exec.stop()
end
fichier = telechargement.file()
_log.info("Nom du fichier téléchargé : #{fichier.name()}")
fichier.save(_app.file("storage/filesystem/server/#{fichier.name()}"))
fichier.save(_storage.filesystem("server", "autre.png").file())
fichier.save(_os.file("/tmp/telechargement.png"))
_out.json(_val.map().set("resultat", true))
val telechargement = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if (!telechargement.ok()) {
_log.error("Statut du téléchargement du fichier : ${telechargement.statusCode()}\n${telechargement.toString()}")
_header.status(500)
_out.json(_val.map().set("resultat", false).set("erreur", "echec-telechargement-fichier"))
_exec.stop()
}
fichier = telechargement.file()
_log.info("Nom du fichier téléchargé : ${fichier.name()}")
fichier.save(_app.file("storage/filesystem/server/${fichier.name()}"))
fichier.save(_storage.filesystem("server", "autre.png").file())
fichier.save(_os.file("/tmp/telechargement.png"))
_out.json(_val.map().set("resultat", true))
final telechargement = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png')
if (!telechargement.ok()) {
_log.error(`Statut du téléchargement du fichier : ${telechargement.statusCode()}\n${telechargement.toString()}`)
_header.status(500)
_out.json({resultat: false, erreur: 'echec-telechargement-fichier'})
_exec.stop()
}
final fichier = telechargement.file()
_log.info(`Nom du fichier téléchargé : ${fichier.name()}`)
fichier.save(_app.file(`storage/filesystem/server/${fichier.name()}`))
fichier.save(_storage.filesystem("server", "autre.png").file())
fichier.save(_os.file("/tmp/telechargement.png"))
_out.json({resultat: true})
Le
fichier.saveest utilisé 3 fois volontairement, pour en savoir plus sur_app.file,_storageet_os.file, consultez le tutoriel sur Manipulation de fichiers et dossiers.
Téléversement
Pour envoyer un fichier structuré comme formulaire au format multipart, nous utilisons asMultipartFormData().
Voyez l'exemple complet d'un service :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const fichier = _app.file("public/images/logo.png");
const televersement = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("fichier", fichier)
);
if (televersement.ok()) {
_log.info(`Téléversé avec succès : ${fichier.name()}`);
_out.json({resultat: true});
} else {
_log.error(`Statut du téléversement du fichier : ${televersement.statusCode()}\n${televersement.toString()}`);
_header.status(500);
_out.json({resultat: false, erreur: 'echec-televersement-fichier'});
}
const fichier = _app.file("public/images/logo.png");
const televersement = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("fichier", fichier)
);
if (televersement.ok()) {
_log.info(`Téléversé avec succès : ${fichier.name()}`);
_out.json({resultat: true});
} else {
_log.error(`Statut du téléversement du fichier : ${televersement.statusCode()}\n${televersement.toString()}`);
_header.status(500);
_out.json({resultat: false, erreur: 'echec-televersement-fichier'});
}
fichier = _app.file("public/images/logo.png")
televersement = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("fichier", fichier)
)
if televersement.ok():
_log.info(f"Téléversé avec succès : {fichier.name()}")
_out.json(_val.map().set("resultat", True))
else:
_log.error(f"Statut du téléversement du fichier : {televersement.statusCode()}\n{televersement.toString()}")
_header.status(500)
_out.json(_val.map().set("resultat", False).set("erreur", "echec-televersement-fichier"))
fichier = _app.file("public/images/logo.png")
televersement = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("fichier", fichier)
)
if televersement.ok()
_log.info("Téléversé avec succès : #{fichier.name()}")
_out.json(_val.map().set("resultat", true))
else
_log.error("Statut du téléversement du fichier : #{televersement.statusCode()}\n#{televersement.toString()}")
_header.status(500)
_out.json(_val.map().set("resultat", false).set("erreur", "echec-televersement-fichier"))
end
val fichier = _app.file("public/images/logo.png")
val televersement = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("fichier", fichier)
)
if (televersement.ok()) {
_log.info("Téléversé avec succès : ${fichier.name()}")
_out.json(_val.map().set("resultat", true))
} else {
_log.error("Statut du téléversement du fichier : ${televersement.statusCode()}\n${televersement.toString()}")
_header.status(500)
_out.json(_val.map().set("resultat", false).set("erreur", "echec-televersement-fichier"))
}
final fichier = _app.file("public/images/logo.png")
final televersement = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("fichier", fichier)
)
if (televersement.ok()) {
_log.info(`Téléversé avec succès : ${fichier.name()}`)
_out.json({resultat: true})
} else {
_log.error(`Statut du téléversement du fichier : ${televersement.statusCode()}\n${televersement.toString()}`)
_header.status(500)
_out.json({resultat: false, erreur: 'echec-televersement-fichier'})
}
Remarquez que dans l'exemple ci-dessus, le fichier est envoyé vers un autre service, voyez comment cet autre service qui reçoit le fichier peut être implémenté dans Netuno :
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const fichier = _req.getFile("fichier");
_log.info(`Nom du fichier reçu : ${fichier.name()}`);
fichier.save(_storage.filesystem("server", "recu.png").file());
_out.json({resultat: true});
const fichier = _req.getFile("fichier");
_log.info(`Nom du fichier reçu : ${fichier.name()}`);
fichier.save(_storage.filesystem("server", "recu.png").file());
_out.json({resultat: true});
fichier = _req.getFile("fichier")
_log.info(f"Nom du fichier reçu : {fichier.name()}")
fichier.save(_storage.filesystem("server", "recu.png").file())
_out.json(_val.map().set("resultat", True))
fichier = _req.getFile("fichier")
_log.info("Nom du fichier reçu : #{fichier.name()}")
fichier.save(_storage.filesystem("server", "recu.png").file())
_out.json(_val.map().set("resultat", true))
val fichier = _req.getFile("fichier")
_log.info("Nom du fichier reçu : ${fichier.name()}")
fichier.save(_storage.filesystem("server", "recu.png").file())
_out.json(_val.map().set("resultat", true))
final fichier = _req.getFile("fichier")
_log.info(`Nom du fichier reçu : ${fichier.name()}`)
fichier.save(_storage.filesystem("server", "recu.png").file())
_out.json({resultat: true})
Conclusion
Avec la ressource Remote, nous pouvons intégrer des API externes, nous pouvons exporter, importer ou synchroniser des données.
Nous pouvons même intégrer des services implémentés dans d'autres technologies, ce qui permet de réaliser des opérations spécifiques fournies par d'autres langages.