Skip to content

Minimal SOAP 1.1 client: posts an XML envelope to an endpoint and gives back the response body as a string, plus an XPath helper to pick values out of it. There is no WSDL support; build the envelope yourself. Things to know: only the body text is returned, so the HTTP status is not available, and a SOAP Fault (usually HTTP 500) comes back like any other response, so check the XML for a Fault. Network failures raise "Api request failed: ...". There are no timeout, cookie or authentication helpers; add an Authorization header through the headers argument. TLS certificates are always verified; use clientCertificate() for mutual TLS. Credentials belong in secrets: secret("name").

  • Kind Library, a global in every script
  • Category APIs & Data
  • Functions 4

Functions

  • post string Posts a SOAP envelope to the endpoint and returns the response body as a string.
  • clientCertificate self Registers a PKCS#12 (.p12/.pfx) client certificate for mutual TLS.
  • cert self Alias for clientCertificate(host, port, file, passphrase).
  • xpath string Evaluates an XPath 1.0 expression on an XML string and returns the result as a string: the text of the first matching node, or the string form of a number, boolean or string expression (count(//Item) gives "3").
Soap.post(soapUrl: string, requestMessage: string, SOAPAction: string, doFormat: boolean)→stringSoap.post(soapUrl: string, requestMessage: string, SOAPAction: string, headers: table, doFormat: boolean)→string

Posts a SOAP envelope to the endpoint and returns the response body as a string. Sets the headers "Content-Type: text/xml;charset=UTF-8" and "SOAPAction: ACTION" (the action is sent as given, without added quotes; pass "" for none). Entries in the optional headers table replace those defaults, which is how you switch to SOAP 1.2 ("Content-Type: application/soap+xml") or add an Authorization header. The request is encoded as UTF-8, the response is read as UTF-8 and carriage returns are removed. The last argument is required: with doFormat true the response is pretty-printed, with false it is returned as received. Formatting only changes the indentation; entities such as & keep their meaning. With four arguments the fourth is always doFormat; to pass headers use all five. The HTTP status is not returned, and error statuses do not raise: a SOAP Fault is just the response body. Connection problems and timeouts raise "Api request failed: ...".

Parameters

soapUrl string
The URL of the SOAP endpoint
requestMessage string
The complete SOAP envelope as an XML string
SOAPAction string
Value of the SOAPAction header; "" for none
headers table
Extra request headers. A map ({["Content-Type"] = "application/soap+xml"}) replaces the default header of that name; a list of "Name:value" strings adds headers
doFormat boolean
true to pretty-print the response, false to return it as received

Returns

string The response body, formatted when doFormat is true

Example

Call a service with an Authorization header and check the result

local envelope = [[
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ord="urn:orders">
<soapenv:Body>
<ord:GetOrder><ord:OrderId>1001</ord:OrderId></ord:GetOrder>
</soapenv:Body>
</soapenv:Envelope>]]
local response = Soap.post(
"https://erp.example.com/services/orders",
envelope,
"urn:orders/GetOrder",
{ Authorization = "Bearer " .. secret("erp_token") },
false)
Assert.equals("", Soap.xpath(response, "//*[local-name()='Fault']"))
print(Soap.xpath(response, "//*[local-name()='Status']"))
Soap.clientCertificate(host: string, port: number, file: string, passphrase: string)→self

Registers a PKCS#12 (.p12/.pfx) client certificate for mutual TLS. It is presented only on requests to the given host and port (a URL without a port means 443 for https and 80 for http); calling it again for the same host and port replaces the certificate. The file is read from the project (fetched from the Reflex server when it is not in the workspace). A copy fetched from the server is removed from the workspace after loading; a file that was already in the workspace is left alone. Raises "Failed to configure client certificate: ..." when the port is outside 1-65535, the file is missing, or the passphrase is wrong. Use secret() for the passphrase.

Parameters

host string
Host name of the HTTPS endpoint, without scheme or port
port number
Port of the endpoint, 1-65535
file string
PKCS#12 file in the project
passphrase string
Passphrase of the file; nil is treated as an empty one

Returns

self chainable The library itself, so calls can be chained.

Example

Soap.clientCertificate("soap.partner.example", 443, "certs/client.p12", secret("client_cert_passphrase"))
local response = Soap.post("https://soap.partner.example/service", "<Envelope/>", "", false)
Soap.cert(host: string, port: number, file: string, passphrase: string)→self

Alias for clientCertificate(host, port, file, passphrase).

Parameters

host string
Host name of the HTTPS endpoint, without scheme or port
port number
Port of the endpoint, 1-65535
file string
PKCS#12 file in the project
passphrase string
Passphrase of the file; nil is treated as an empty one

Returns

self chainable The library itself, so calls can be chained.
Soap.xpath(in: string, xpath: string)→stringSoap.xpath(in: string, xpath: string, namespaces?: table)→string

Evaluates an XPath 1.0 expression on an XML string and returns the result as a string: the text of the first matching node, or the string form of a number, boolean or string expression (count(//Item) gives "3"). Returns "" when nothing matches. Raises an error when the XML cannot be parsed or the expression is invalid. The XML is parsed with namespace support. Prefixes declared in the document can be used in the expression (//ord:OrderId), and a third argument maps extra prefixes to namespace URIs, which is the way to address elements in a default namespace (xmlns="urn:orders") because XPath 1.0 has no default namespace. Matching on the local name keeps working: //*[local-name()='OrderId']. A name without a prefix only matches elements that are in no namespace. Entities are decoded, so an ampersand comes back as "&".

Parameters

in string
The XML document as a string
xpath string
The XPath 1.0 expression
namespaces table optional
Extra namespace prefixes as a table `{ prefix = "uri" }`, added to the prefixes declared in the document

Returns

string The result of the XPath expression, or "" when nothing matches

Example

local xml = '<Envelope><Body><GetOrderResponse><OrderId>42</OrderId><Status>SHIPPED</Status></GetOrderResponse></Body></Envelope>'
print(Soap.xpath(xml, "//OrderId"))
print(Soap.xpath(xml, "count(//*)"))
local ns = '<o:Order xmlns:o="urn:orders"><o:Id>7</o:Id></o:Order>'
print(Soap.xpath(ns, "//o:Id"))
print(Soap.xpath('<Order xmlns="urn:orders"><Id>8</Id></Order>', "//x:Id", { x = "urn:orders" }))