Simple Object Access Protocol (SOAP) is a RPC-style web service protocol. Request and response are encoded in standardised SOAP XML, and can be sent over any transport layer protocol (HTTP, FTP, TCP, POP3 etc.). Any server can be turned into a SOAP server by:
- Writing a SOAP service layer
This layer will intercept all incoming requests, transcode the XML into application’s data type and pass it on to the actual request handlers. Same with responses. Get response from request handlers, transcode it into SOAP-compliant XML and send away. - Publishing a WSDL file
A Web Service Description Language (WSDL) file uses a standardised XML grammar to describe the RPC methods available and how to call them. It’s akin to aprotobuf
service definition when working with Google’sgRPC
framework. The WSDL file can automagically setup clients to consume the SOAP service.
The first part of SOAP specification outlines a standard envelope format for packaging data. The second part of the specification outlines one possible method of serialising the data intended for packaging. These rules outline how basic application data types are to be mapped and encoded into XML format when embedded into a SOAP envelope.
Encoding data
SOAP envelopes are designed to carry any arbitrary XML documents no matter what the body of the message looks like, or whether it conforms to any specific set of data encoding rules. The rules discussed here are offered only as a convenience to allow applications to dynamically exchange information without prior knowledge of the types of information to be exchanged.
Terminology
Accessors and values
<firstname> <!-- accessor -->
Joe <!-- value -->
</firstname>
Compound values
Two types:
struct
, a compound value in which each accessor has a different namearray
, a compound value in which the accessors have the same name (values are identified by array indices)
<!-- A struct -->
<person>
<firstname>Animesh</firstname>
<lastname>Mishra</lastname>
</person>
<!-- An array -->
<person>
<person name="Animesh Mishra">
<person name="Abhishek Mishra">
</person>
Single-reference and multi-referenced accessors
Through the use of id
and href
attributes, SOAP defines that accessors may either be single-referenced or multi-referenced.
A single-referenced accessor doesn’t have an identity except as a child of its parent element.
<people>
<person name="joe smith">
<address> <!-- single-referenced -->
<street>111 First Street</street>
<city>New York</city>
<state>New York</state>
</address>
</person>
</people>
A multi-referenced accessor uses an id
to give an identity to its value. Other accessors can use the href
attribute to refer to the accessor’s value. In the example below, each person has the same address.
<people>
<person name='joe smith'>
<address href='#address-1'/>
</person>
<person name='john doe'>
<address href='#address-1'/>
</person>
</people>
<address id='address-1'>
<street>111 First Street</street>
<city>New York</city>
<state>New York</state>
</address>
This approach can be used to allow an accessor to reference external information sources that are not part of the SOAP Envelope (binary data, XML schema definition etc.)
<person name='joe smith'>
<address href='http://acme.com/data.xml#joe_smith' />
</person>
SOAP Data types
The data types supported by the SOAP encoding style are the data types defined by the XML Schema data types specification. All the data types used within a SOAP-encoded block of XML must either be taken directly from the XML Schema specification or derived from types therein.
SOAP defines four different ways to express the data type of an accessor:
- Use the
xsi:type
attribute on each accessor, explicitly referencing the data type according to the XML Schema specification.
<person>
<name xsi:type="xsd:string">John Doe</name>
</person>
- Reference an XML Schema document that defines the exact data type of a particular element within its definition.
<person xmlns="personschema.xsd">
<name>John Doe</name>
</person>
- Reference some other type of schema document that defines the data types of a particular element within its definition.
<person xmlns="urn:some_namespace">
<name>John Doe</name>
</person>
<!--
where "urn:some_namespace" indicates some namespace in which
the value of name elements are strings
-->
- Anonymous accessor style. It’s anonymous because the accessor’s name is its type, rather than a meaningful identification for the value. This is commonly found in SOAP encoded arrays.
<SOAP-ENC:int>36</SOAP-ENC:int>
<!-- similar to <value xsi:type="xsd:int">36</value> -->
Packaging data
<!-- RPC-style SOAP request -->
<s:Envelope xmlns:s="http://www.w3.org/2001/06/soap-envelope">
<s:Header>
<m:transaction xmlns:m="soap-transaction" s:mustUnderstand="true">
<transactionID>1234</transactionID>
</m:transaction>
</s:Header>
<s:Body>
<n:getQuote xmlns:n="urn:QuoteService">
<symbol xsi:type="xsd:string">
IBM
</symbol>
</n:getQuote>
</s:Body>
</s:Envelope>
<!-- RPC-style SOAP response -->
<s:Envelope xmlns:s="http://www.w3.org/2001/06/soap-envelope">
<s:Body>
<n:getQuoteRespone xmlns:n="urn:QuoteService">
<value xsi:type="xsd:float">
98.06
</value>
</n:getQuoteResponse>
</s:Body>
</s:Envelope>
The XML schema for a SOAP message is based on the http://www.w3.org/2001/06/soap-envelope
namespace. A SOAP message consists of an envelope containing:
- a header
Optional. Contains blocks of information relevant to how the message is to be processed. This includes routing and delivery settings, authentication/authorisation etc. - a body
Contains the actual message to be delivered and processed. A body is required.
Faults
A SOAP fault is a special type of message specifically targeted at communicating information about errors that may have occurred during the processing of a SOAP message.
<s:Envelope xmlns:s="...">
<s:Body>
<s:Fault>
<faultcode>Client.Authentication</faultcode>
<faultstring>
Invalid credentials
</faultstring>
<faultactor>http://acme.com</faultactor>
<details>
<!-- application specific details -->
</details>
</s:Fault>
</s:Body>
</s:Envelope>
Remote procedure calls
Remote procedure calls allow one node in a distributed system to trigger a function (or procedure) call in another part of the network.
Request
Rules for packaging an RPC request in a SOAP envelope are simple:
- Method call is represented as a single XML struct with each in or in-out parameter modelled as a field in that structure
- Names and order of parameters must correspond to the names and order of parameters in the method being invoked.
So a Swift method with the following signature:
func checkStatus(orderCode: String, customerID: String) -> String
can be invoked with these arguments:
let result = checkStatus(orderCode: "abc123", customerID: "Bob's Store")
using the following SOAP envelope:
<s:Envelope xmlns:s="...">
<s:Body>
<checkStatus xmlns="..." s:encodingStyle="...">
<orderCode xsi:type="string">abc123</orderCode>
<customerID xsi:type="string">
Bob's Store
</customerID>
</checkStatus>
</s:Body>
</s:Envelope>
Response
Responses are similar to method calls in that the structure of the response is modelled as a single XML structure with a field for each in-out or out parameter in the method signature. So, if the Swift method call above returned “Delivered”, the SOAP response might be something like:
<s:Envelope xmlns:s="...">
<s:Body>
<checkStatusResponse s:encodingStyle="...">
<return xsi:type="xsd:string">Delivered</return>
</checkStatusResponse>
</s:Body>
</s:Envelope>
The name of the message response structure checkStatusResponse
is not important but the convention is to name it after the method with Response appended. Similarly, the name of the return element is arbitrary – the first field in the message response structure is assumed to be the return value.
Transport
SOAP fits on the web services technology stack as a standardised packaging protocol layered on top of the network and transport layers. As a packaging protocol, SOAP does not care what transport protocols are used to exchange the messages.
Because of its pervasiveness, HTTP is by far the most common transport used to exchange SOAP messages. It’s a natural match with SOAP’s RPC conventions because HTTP itself is request-response-based protocol.
HTTP request containing a SOAP message
The SOAPAction HTTP header is defined by the SOAP specification, and indicates the intent of the SOAP HTTP request. Its value is completely arbitrary, but it’s intended to tell the HTTP server what the SOAP message wants to do before the HTTP server decodes the XML. Servers can then use the SOAPAction header to filter unacceptable requests.
POST /StockQuote HTTP/1.1
Content-Type: text/xml
Content-Length: nnn
SOAPAction: "urn:StockQuote#GetQuote"
<s:Envelope xmlns:s="http://www.w3.org/2001/06/soap-envelope">
...
</s:Envelope>
HTTP response containing a SOAP message
HTTP/1.1 200 OK
Content-Type: text/xml
Content-Length: nnn
<s:Envelope xmlns:s="http://www.w3.org/2001/06/soap-envelope">
...
</s:Envelope>
Reporting errors
The HTTP 500 Server Error response is the default response required for all SOAP faults, regardless of the fault code. Despite the fact that some faults (such as bad requests) are not Server Errors, the 500 Server Error code is still the right response when HTTP is used for the transport.