Καταλαβαίνουμε το SOAP βήμα βήμα, με ένα απλό παράδειγμα προϊόντος, XML και JSON, λίγο C# και μια καθαρή εικόνα της Clean Architecture.
Έχεις ένα ηλεκτρονικό κατάστημα και θέλεις να εμφανίσεις την τιμή ενός προϊόντος. Η τιμή βρίσκεται στο σύστημα του προμηθευτή σου. Η εφαρμογή σου πρέπει να τη ζητήσει, να πάρει μια απάντηση και να εμφανίσει το σωστό ποσό στον πελάτη.
Ας υποθέσουμε ότι ο προμηθευτής χρησιμοποιεί SOAP. Θα ακολουθήσουμε αυτή τη μία επικοινωνία από την αρχή μέχρι το τέλος: ζητάμε την τιμή του προϊόντος 123 και παίρνουμε την απάντηση 19.90 EUR.
Το SOAP είναι ένας συμφωνημένος τρόπος επικοινωνίας μεταξύ εφαρμογών. Η λέξη «πρωτόκολλο» σημαίνει εδώ ότι οι δύο πλευρές ακολουθούν κοινούς κανόνες για τα μηνύματα που ανταλλάσσουν. Το SOAP χρησιμοποιεί XML για τη μορφή αυτών των μηνυμάτων. Εισαγωγή του W3C στο SOAP.
Στο παράδειγμά μας, η επικοινωνία είναι αυτή:
Το κατάστημά μας Ο προμηθευτής
«Πόσο κοστίζει το προϊόν 123;» ────────►
◄──────── «Κοστίζει 19,90 ευρώ»
Οι εφαρμογές μεταφέρουν αυτή την ερώτηση και την απάντηση σε μια συγκεκριμένη δομή. Πρώτα θα καταλάβουμε την XML και μετά θα τη βάλουμε μέσα σε ένα μήνυμα SOAP.
Η XML οργανώνει τις πληροφορίες με ετικέτες. Για παράδειγμα:
<Product>
<Id>123</Id>
<Price>19.90</Price>
</Product>
Μπορούμε να το διαβάσουμε σαν πρόταση: «Υπάρχει ένα προϊόν. Ο κωδικός του είναι 123 και η τιμή του είναι 19.90».
Το <Product> ανοίγει το στοιχείο του προϊόντος και το </Product> το κλείνει. Μέσα του υπάρχουν δύο μικρότερα στοιχεία: το Id και το Price. Αυτή η σχέση δημιουργεί μια δομή σαν δέντρο, όπου κάθε στοιχείο μπορεί να περιέχει άλλα στοιχεία ή μια τιμή.
Τα ονόματα έχουν σημασία: το <Price> είναι διαφορετικό από το <price>, επειδή η XML ξεχωρίζει πεζά και κεφαλαία. Επίσης, κάθε στοιχείο πρέπει να κλείνει σωστά και τα στοιχεία να μπαίνουν το ένα μέσα στο άλλο με σωστή σειρά. Βασικοί κανόνες XML.
Η XML επιτρέπει και attributes, δηλαδή πρόσθετες πληροφορίες μέσα στο αρχικό tag:
<Price currency="EUR">19.90</Price>
Εδώ το 19.90 είναι το περιεχόμενο του στοιχείου Price. Το currency="EUR" είναι ένα attribute που μας λέει ότι το ποσό είναι σε ευρώ.
Το SOAP βάζει την πληροφορία μέσα σε έναν τυποποιημένο φάκελο. Στο δικό μας αίτημα θέλουμε να καλέσουμε τη λειτουργία GetPrice, δηλαδή «βρες τιμή», για το προϊόν 123:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:shop="urn:shop">
<soap:Body>
<shop:GetPrice>
<shop:ProductId>123</shop:ProductId>
</shop:GetPrice>
</soap:Body>
</soap:Envelope>
Το διαβάζουμε από έξω προς τα μέσα:
- Το
Envelopeείναι ο εξωτερικός φάκελος του μηνύματος. - Το
Bodyείναι το μέρος που περιέχει την πληροφορία που στέλνουμε. - Το
GetPriceείναι η λειτουργία που ζητάμε από την υπηρεσία. - Το
ProductIdδίνει τον κωδικό του προϊόντος:123.
Το SOAP ορίζει τα Envelope και Body. Ο προμηθευτής ορίζει τα GetPrice και ProductId, σύμφωνα με τη δική του υπηρεσία. Έτσι ξέρουμε ποιο κομμάτι αφορά τον γενικό τρόπο επικοινωνίας και ποιο αφορά τη συγκεκριμένη δουλειά που θέλουμε να γίνει. Δομή μηνύματος SOAP.
Μπορεί να βλέπεις και ένα Header πριν από το Body. Είναι προαιρετικό και μεταφέρει πρόσθετες πληροφορίες για την επεξεργασία του μηνύματος. Στο απλό μας παράδειγμα δεν το χρειαζόμαστε.
Τα soap: και shop: είναι μικρά αναγνωριστικά που ξεχωρίζουν δύο ομάδες στοιχείων. Το soap: συνδέεται με τους κανόνες του SOAP και το shop: με τα στοιχεία της υπηρεσίας μας. Οι δηλώσεις xmlns δείχνουν σε ποιο namespace, δηλαδή σε ποια ομάδα ονομάτων, ανήκει κάθε prefix.
Κράτησε αυτή τη λεπτομέρεια για την ανάγνωση του XML: κάποιος μπορεί να χρησιμοποιεί s: αντί για soap:. Εφόσον το συνδέει με το ίδιο namespace, η σημασία παραμένει ίδια. Ο κώδικάς μας θα αναγνωρίζει το namespace και το όνομα του στοιχείου. Namespaces στην XML.
Το αίτημα φτάνει στη διεύθυνση της υπηρεσίας. Αυτή η διεύθυνση ονομάζεται endpoint. Στο παράδειγμά μας, η εφαρμογή στέλνει το XML με HTTP POST. Η υπηρεσία διαβάζει τον κωδικό 123, αναζητά την τιμή και επιστρέφει ένα νέο XML μήνυμα.
Χρησιμοποιούμε τη μορφή SOAP 1.1 σε όλα τα παραδείγματα, ώστε να ακολουθούμε σταθερά τους ίδιους κανόνες. Για μια πραγματική υπηρεσία, ο πάροχος δίνει το endpoint και τις απαιτούμενες ρυθμίσεις αποστολής. Μεταφορά SOAP 1.1 μέσω HTTP.
Η απάντηση της υποθετικής υπηρεσίας μας είναι:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:shop="urn:shop">
<soap:Body>
<shop:GetPriceResponse>
<shop:Price currency="EUR">19.90</shop:Price>
</shop:GetPriceResponse>
</soap:Body>
</soap:Envelope>
Ο φάκελος παραμένει ίδιος. Το περιεχόμενο του Body έχει αλλάξει: τώρα περιέχει GetPriceResponse, δηλαδή την απάντηση στη λειτουργία που καλέσαμε.
Η πληροφορία που χρειαζόμαστε βρίσκεται εδώ:
<shop:Price currency="EUR">19.90</shop:Price>
Άρα, η τιμή είναι 19.90 και το νόμισμα EUR. Στο XML η δεκαδική τιμή γράφεται με τελεία· στην οθόνη μπορούμε να την εμφανίσουμε ως 19,90 €. Η διαδρομή που ακολουθούμε για να τη βρούμε είναι:
Envelope → Body → GetPriceResponse → Price
Αυτή είναι και η διαδρομή που θα ακολουθήσει ο κώδικάς μας.
Αν κάτι αποτύχει, η απάντηση μπορεί να περιέχει Fault. Για παράδειγμα, ο προμηθευτής μπορεί να επιστρέψει ένα SOAP σφάλμα όταν δεν μπορεί να επεξεργαστεί το αίτημα. Το Fault βρίσκεται μέσα στο Body και περιγράφει το πρόβλημα. Ένας κανονικός SOAP client ελέγχει και αυτή την περίπτωση. Παρακάτω θα διαβάσουμε την επιτυχημένη απάντηση του παραδείγματος. Σφάλματα SOAP.
Στο C# διαβάζουμε την απάντηση σαν δέντρο. Ας υποθέσουμε ότι η HTTP κλήση έχει ήδη ολοκληρωθεί και το XML της απάντησης βρίσκεται σε μια μεταβλητή responseXml, ως κείμενο.
using System;
using System.Xml;
using System.Xml.Linq;
var document = XDocument.Parse(responseXml);
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace shop = "urn:shop";
var priceElement = document.Root?
.Element(soap + "Body")?
.Element(shop + "GetPriceResponse")?
.Element(shop + "Price");
if (priceElement is null)
throw new InvalidOperationException("Δεν βρέθηκε τιμή στην απάντηση.");
decimal price = XmlConvert.ToDecimal(priceElement.Value);
string currency = priceElement.Attribute("currency")?.Value ?? "";
Console.WriteLine($"Τιμή: {price} {currency}");
Ας το δούμε βήμα βήμα:
- Το
XDocument.Parse(responseXml)διαβάζει το κείμενο και δημιουργεί ένα δέντρο XML. - Τα δύο
XNamespaceαντιστοιχούν στα namespaces που είδαμε στην απάντηση. - Το
document.Rootμάς δίνει το εξωτερικό στοιχείο, τοEnvelope. - Με κάθε
Element(...)κατεβαίνουμε ένα επίπεδο: στοBody, μετά στοGetPriceResponseκαι τέλος στοPrice. - Το
priceElement.Valueδιαβάζει το κείμενο19.90. ΤοXmlConvert.ToDecimalτο μετατρέπει σε αριθμό τύπουdecimal. - Το
Attribute("currency")διαβάζει το νόμισμαEUR.
Τα ? στην αλυσίδα επιτρέπουν στην αναζήτηση να σταματήσει αν λείπει κάποιο στοιχείο. Ο έλεγχος if μάς δίνει τότε ένα κατανοητό σφάλμα, αντί να συνεχίσουμε σαν να βρήκαμε τιμή.
Έτσι, από ένα μεγάλο XML μήνυμα καταλήγουμε σε δύο απλές τιμές της εφαρμογής: έναν αριθμό και ένα νόμισμα. Ανάγνωση XML με LINQ to XML, Μετατροπή σε decimal.
Το JSON μπορεί να περιγράψει την ίδια πληροφορία με διαφορετική γραφή. Η τιμή του προϊόντος μας θα μπορούσε να εμφανίζεται έτσι:
{
"price": 19.90,
"currency": "EUR"
}
Το XML χρησιμοποιεί tags και attributes. Το JSON χρησιμοποιεί ονόματα πεδίων και τιμές. Για ένα τόσο απλό μήνυμα, το JSON είναι συνήθως πιο σύντομο και διαβάζεται εύκολα ως αντικείμενο.
| Αυτό που θέλουμε να περιγράψουμε | XML | JSON |
|---|---|---|
| Τιμή προϊόντος | Περιεχόμενο του στοιχείου Price
|
Τιμή του πεδίου price
|
| Νόμισμα | Attribute currency="EUR"
|
Πεδίο "currency": "EUR"
|
| Οργάνωση | Στοιχεία μέσα σε άλλα στοιχεία | Αντικείμενα και πίνακες |
Η βασική διάκριση είναι η εξής: XML και JSON είναι μορφές δεδομένων. Το SOAP είναι πρωτόκολλο επικοινωνίας που χρησιμοποιεί XML. Το JSON από μόνο του δεν ορίζει πού στέλνουμε ένα μήνυμα ή ποια λειτουργία θα εκτελέσει ο παραλήπτης. Προδιαγραφή JSON.
Το WSDL μάς λέει τι περιμένει η υπηρεσία. Επιστρέφοντας στον προμηθευτή μας, πρέπει κάπως να μάθουμε ότι η λειτουργία λέγεται GetPrice, ότι δέχεται ProductId και ποια μορφή έχει η απάντηση.
Αυτές τις πληροφορίες περιγράφει το WSDL, ένα αρχείο που λειτουργεί σαν τεχνικός οδηγός της υπηρεσίας. Περιλαμβάνει τις διαθέσιμες λειτουργίες, τα μηνύματα που ανταλλάσσονται και τις διευθύνσεις επικοινωνίας. Έτσι, ο προγραμματιστής μπορεί να υλοποιήσει τη σύνδεση βάσει συγκεκριμένης συμφωνίας. Περιγραφή υπηρεσιών με WSDL.
Συχνά το project περιέχει ήδη έναν client που έχει δημιουργηθεί από το WSDL. Τον αναγνωρίζεις από τις μεθόδους που αντιστοιχούν στις λειτουργίες της υπηρεσίας. Αυτός αναλαμβάνει την κατασκευή και την ανάγνωση των μηνυμάτων, ώστε ο υπόλοιπος κώδικας να καλεί συνηθισμένες μεθόδους C#. Δημιουργία SOAP client σε .NET.
Τα DTD και XSD αφορούν τους κανόνες των δεδομένων. Ένα XML μπορεί να είναι σωστά γραμμένο, αλλά να λείπει μια πληροφορία που απαιτεί η υπηρεσία. Για παράδειγμα, το αίτημα μπορεί να έχει σωστά tags αλλά να μην περιέχει κωδικό προϊόντος.
Το DTD είναι ένας τρόπος περιγραφής της επιτρεπόμενης δομής ενός XML εγγράφου. Μπορεί να ορίζει ποια στοιχεία πρέπει να υπάρχουν και με ποια σειρά. Στα μηνύματα SOAP, όμως, απαγορεύεται η δήλωση DOCTYPE, μέσω της οποίας δηλώνεται DTD. DTD στην XML, Περιορισμός στα μηνύματα SOAP.
Στα συμβόλαια SOAP συναντάμε συνήθως XSD. Το XSD μπορεί να ορίζει και τύπους δεδομένων: για παράδειγμα, ότι η τιμή είναι δεκαδικός αριθμός. Έτσι, όπου εφαρμόζεται επικύρωση με αυτό το schema, η τιμή 19.90 μπορεί να γίνει δεκτή, ενώ το κείμενο δεκαεννέα ευρώ απορρίπτεται από το ίδιο αριθμητικό πεδίο. Εισαγωγή στο XML Schema.
Οι τρεις έννοιες συνδέονται με το παράδειγμά μας ως εξής:
| Έννοια | Τι μας λέει για την υπηρεσία τιμών |
|---|---|
| SOAP | Πώς οργανώνουμε το μήνυμα που ανταλλάσσουμε. |
| WSDL | Ποια λειτουργία καλούμε και πώς επικοινωνούμε μαζί της. |
| XSD | Ποια στοιχεία και ποιους τύπους δεδομένων περιμένει το συμβόλαιο. |
Στην Clean Architecture δίνουμε στον κώδικα SOAP μια συγκεκριμένη θέση. Η εφαρμογή του καταστήματος χρειάζεται την τιμή του προϊόντος. Ο τρόπος με τον οποίο μιλάμε με τον προμηθευτή ανήκει στη σύνδεση με το εξωτερικό σύστημα.
Μπορούμε να οργανώσουμε αυτή τη μικρή διαδρομή ως εξής:
| Μέρος της εφαρμογής | Ευθύνη στο παράδειγμά μας |
|---|---|
| Application | Ορίζει ότι χρειάζεται να μπορεί να ζητήσει την τιμή ενός προϊόντος. |
| Infrastructure | Καλεί την υπηρεσία SOAP και μετατρέπει την απάντηση σε δεδομένα της εφαρμογής. |
| API | Δέχεται το αίτημα του δικού μας χρήστη και επιστρέφει το αποτέλεσμα. |
Στο Application μπορούμε να έχουμε ένα interface, δηλαδή μια συμφωνία για τη λειτουργία «βρες τιμή». Η κλάση που υλοποιεί τη σύνδεση SOAP βρίσκεται στο Infrastructure και ακολουθεί αυτή τη συμφωνία. Ο κώδικας ανάγνωσης XML που είδαμε ανήκει εκεί, μαζί με την υπόλοιπη επικοινωνία με τον προμηθευτή.
Η βασική αρχή είναι ότι ο κώδικας της επιχειρηματικής λειτουργίας εξαρτάται από αυτή τη συμφωνία. Η υλοποίηση της εξωτερικής σύνδεσης εξαρτάται επίσης από τη συμφωνία του Application. Με αυτόν τον τρόπο, η επιχειρηματική λειτουργία μπορεί να χρησιμοποιήσει διαφορετική υλοποίηση όταν αλλάξει ο τρόπος επικοινωνίας. Η αρχή των εξαρτήσεων στην Clean Architecture.
Η διαδρομή των δεδομένων είναι πλέον ξεκάθαρη:
Ο χρήστης ζητά την τιμή
↓
Η εφαρμογή ζητά τα δεδομένα από τον προμηθευτή
↓
Η σύνδεση SOAP στέλνει το XML και διαβάζει την απάντηση
↓
Η εφαρμογή παίρνει την τιμή 19.90 και το νόμισμα EUR
↓
Ο χρήστης βλέπει 19,90 €
Οι εταιρείες χρησιμοποιούν SOAP όταν εξυπηρετεί τις διασυνδέσεις τους. Μπορεί ο προμηθευτής να διαθέτει αυτή τη συγκεκριμένη υπηρεσία ή μια εταιρεία να έχει ήδη πολλές λειτουργικές συνδέσεις που βασίζονται σε αυτή. Η αλλαγή απαιτεί συντονισμό και ελέγχους, οπότε χρειάζεται αντίστοιχο όφελος για την επιχείρηση.
Για τον προγραμματιστή που αναλαμβάνει τη σύνδεση, το πρακτικό σημείο εκκίνησης είναι το ίδιο: βρίσκει ποια λειτουργία πρέπει να καλέσει, τι περιμένει το αίτημα και πού βρίσκεται η πληροφορία στην απάντηση. Στο δικό μας παράδειγμα, όλη αυτή η επικοινωνία εξυπηρετεί μία απλή ανάγκη: να εμφανιστεί η σωστή τιμή του προϊόντος στην οθόνη.
Top comments (0)