In the 2000s, systems talked in SOAP messages described by WSDL contracts, with the simpler XML-RPC before them. JSON APIs replaced them for new work, but banks, airlines, governments and enterprise suites still expose SOAP, so ingestion jobs need a client: zeep 4.3.3 (github.com/mvantellingen/python-zeep (https://github.com/mvantellingen/python-zeep 2,010 ), MIT; pip 21,050 install zeep==4.3.3). A small BookNest pricing service answers SOAP and plain HTTP on port 31081:
import json, threading
from http.server import BaseHTTPRequestHandler as Base, ThreadingHTTPServer
from lxml import etree
BOOKS = {b.findtext("identifier"): b for b in etree.parse("booknest-catalog.xml").iter("book")}
ENV = ('<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">'
'<s:Body>%s</s:Body></s:Envelope>')
class Handler(Base):
def send(self, status, ctype, body):
self.send_response(status); self.send_header("Content-Type", ctype); self.end_headers()
self.wfile.write(body.encode())
def do_GET(self): # REST: /books/<sku>
if (b := BOOKS.get(self.path[7:])) is None:
return self.send(404, "text/plain", "no such book")
if "json" in self.headers.get("Accept", ""): # content negotiation
data = {"sku": b.findtext("identifier"), "price": b.findtext(".//price")}
return self.send(200, "application/json", json.dumps(data))
self.send(200, "application/xml", etree.tostring(b, encoding="unicode"))
def do_POST(self): # SOAP: GetPrice
req = etree.fromstring(self.rfile.read(int(self.headers["Content-Length"])))
sku = req.findtext(".//{urn:booknest:pricing}sku")
if (b := BOOKS.get(sku)) is None: # a fault travels as HTTP 500
return self.send(500, "text/xml", ENV % "<s:Fault><faultcode>s:Client</faultcode>"
f"<faultstring>Unknown SKU {sku}</faultstring></s:Fault>")
stock = str(b.findtext(".//availability") == "in-stock").lower()
self.send(200, "text/xml", ENV % '<GetPriceResponse xmlns="urn:booknest:pricing">'
f'<price>{b.findtext(".//price")}</price><inStock>{stock}</inStock>'
"</GetPriceResponse>")
def log_message(self, *args): pass
def start(): # serve from a background thread
server = ThreadingHTTPServer(("127.0.0.1", 31081), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()
return serverSubsections
Web Services
A web service is a way for two machines to communicate over a network. Different software systems often need to exchange data with each other, and a web service provides a standard way of doing so over the internet.This section covers the following technologies used to describe, exchange, and call web services:
- WSDL - the Web Services Description Language, used to describe the operations a web service offers.
- SOAP - the Simple Object Access Protocol, used to exchange structured information between web services.
- REST - Representational State Transfer, an architectural style for web services built on standards such as HTTP and URI.
- XML-RPC - a protocol that encodes remote procedure calls in XML and transports them over HTTP.