XML Tutorial · XML in the Real World · Lesson 23 of 28
SOAP and WSDL
How SOAP web services exchange XML envelopes, what a WSDL describes, how to read responses and faults, and how SOAP compares with REST.
SOAP messages
SOAP is an XML messaging protocol, usually sent over HTTP POST. Every message is an envelope with an optional header (security tokens, routing) and a body:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>…</soap:Header>
<soap:Body>
<m:GetPrice xmlns:m="urn:example:prices"><m:Sku>MUG-07</m:Sku></m:GetPrice>
</soap:Body>
</soap:Envelope>
- SOAP 1.1 uses the namespace
http://schemas.xmlsoap.org/soap/envelope/and aSOAPActionHTTP header; SOAP 1.2 useshttp://www.w3.org/2003/05/soap-envelopeand theapplication/soap+xmlcontent type. - Errors come back as a
soap:Faultelement in the body (with HTTP status 500). - WS-Security adds signatures, encryption and tokens in the header.
WSDL
A WSDL file is the contract: it describes the operations, their input and output messages (with an XSD
schema), and the endpoint URL. Tools generate client code from it: zeep (Python), SoapClient (PHP), JAX-WS /
Apache CXF (Java), dotnet-svcutil (.NET).
Reading a response
Responses are namespaced, so query with prefixes:
from lxml import etree
response = b"""<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetPriceResponse xmlns:m="urn:example:prices">
<m:Price currency="EUR">12.50</m:Price>
<m:InStock>true</m:InStock>
</m:GetPriceResponse>
</soap:Body>
</soap:Envelope>"""
ns = {"soap": "http://schemas.xmlsoap.org/soap/envelope/", "m": "urn:example:prices"}
doc = etree.fromstring(response)
price = doc.find(".//m:Price", ns)
print("price:", price.text, price.get("currency"))
print("in stock:", doc.findtext(".//m:InStock", namespaces=ns) == "true")
print("fault:", doc.find(".//soap:Fault", ns) is not None)
Output
price: 12.50 EUR in stock: True fault: False
<?php
$response = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetPriceResponse xmlns:m="urn:example:prices">
<m:Price currency="EUR">12.50</m:Price>
<m:InStock>true</m:InStock>
</m:GetPriceResponse>
</soap:Body>
</soap:Envelope>';
$doc = new DOMDocument();
$doc->loadXML($response);
$xp = new DOMXPath($doc);
$xp->registerNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
$xp->registerNamespace("m", "urn:example:prices");
$price = $xp->query("//m:Price")->item(0);
echo "price: ", $price->textContent, " ", $price->getAttribute("currency"), "\n";
echo "in stock: ", $xp->evaluate("string(//m:InStock)") === "true" ? "True" : "False", "\n";
echo "fault: ", $xp->query("//soap:Fault")->length > 0 ? "True" : "False", "\n";
Output
price: 12.50 EUR in stock: True fault: False
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
import java.io.ByteArrayInputStream;
public class Main {
public static void main(String[] args) throws Exception {
String response = """
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetPriceResponse xmlns:m="urn:example:prices">
<m:Price currency="EUR">12.50</m:Price>
<m:InStock>true</m:InStock>
</m:GetPriceResponse>
</soap:Body>
</soap:Envelope>""";
String soap = "http://schemas.xmlsoap.org/soap/envelope/", m = "urn:example:prices";
var factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(response.getBytes("UTF-8")));
Element price = (Element) doc.getElementsByTagNameNS(m, "Price").item(0);
System.out.println("price: " + price.getTextContent() + " " + price.getAttribute("currency"));
String inStock = doc.getElementsByTagNameNS(m, "InStock").item(0).getTextContent();
System.out.println("in stock: " + (inStock.equals("true") ? "True" : "False"));
System.out.println("fault: " + (doc.getElementsByTagNameNS(soap, "Fault").getLength() > 0 ? "True" : "False"));
}
}
Output
price: 12.50 EUR in stock: True fault: False
Run this on your computer — see “Get Started” for setup.
SOAP or REST?
| SOAP | REST with JSON | |
|---|---|---|
| Contract | WSDL + XSD, generated clients | OpenAPI (optional) |
| Errors | soap:Fault |
HTTP status codes and a JSON body |
| Security | WS-Security (message-level) | TLS + OAuth tokens |
| Where you meet it | Banks, payment and travel systems, government, ERP (SAP), older enterprise APIs | Most new web and mobile APIs |
New public APIs are almost always REST or GraphQL, but SOAP integration is a common, well-paid task in enterprises.
Test yourself
Where does a SOAP service put an error?
In a soap:Fault element inside the bodyOnly in the HTTP status lineIn the WSDLIn an HTTP cookie
Show answer
Answer: In a soap:Fault element inside the body
SOAP errors are Fault elements in the Body, typically returned with HTTP 500.