WSDL¶
Auto-generation — no configuration needed. Register a service and the WSDL is generated automatically:
wsdl_bytes = soap_app.get_wsdl()
Served automatically at GET ?wsdl when using AsgiSoapApp or WsgiSoapApp.
For programmatic generation, build_wsdl(defn, address) returns the
wsdl:definitions element for a WsdlDefinition and build_wsdl_string
its serialized form; the NS class exposes the namespace URI constants
(NS.WSDL, NS.XSD, NS.SOAP_ENV, ...) used throughout soapbar.
Applications spanning several namespaces¶
A SoapApplication can serve any number of services. Whenever they share a
__tns__ they also share one document, and ?wsdl returns the whole contract —
this is the common case and nothing else is needed.
A WSDL 1.1 definitions document carries exactly one targetNamespace, so
services declaring different __tns__ values cannot be described by a single
document. soapbar publishes one document per namespace instead, each with its
own port types, bindings and SOAP version:
soap_app.register(BillingService()) # __tns__ = "http://example.com/billing"
soap_app.register(ShippingService()) # __tns__ = "http://example.com/shipping"
soap_app.wsdl_service_names # ['Billing', 'Shipping']
soap_app.get_wsdl() # the first namespace's document
soap_app.get_wsdl("Shipping") # the shipping document
Over HTTP the same selection is GET ?wsdl=Shipping; a name that matches no
registered service answers 404. Bare ?wsdl keeps returning the first
service's document, and that document's wsdl:documentation element names the
alternatives so they are discoverable.
Services sharing a namespace but differing in binding style or SOAP version are
published as separate port types and bindings inside the same document, which
WSDL 1.1 allows — mixing SOAP 1.1 and 1.2 bindings simply declares both soap:
and soap12: prefixes.
Parse an existing WSDL to inspect its structure:
from soapbar import parse_wsdl, parse_wsdl_file
defn = parse_wsdl(wsdl_bytes) # from bytes/str
defn = parse_wsdl_file("service.wsdl") # from file
Custom WSDL override — supply your own WSDL document and skip auto-generation:
soap_app = SoapApplication(custom_wsdl=open("my_service.wsdl", "rb").read())
Remote wsdl:import — SSRF guard — parse_wsdl blocks outbound HTTP fetches by default. wsdl:import elements whose resolved location starts with http:// or https:// raise ValueError unless you explicitly opt in:
# Default — safe for untrusted WSDLs; remote imports raise ValueError
defn = parse_wsdl(wsdl_bytes)
# Opt-in — only when the WSDL source is trusted
defn = parse_wsdl(wsdl_bytes, allow_remote_imports=True)
This prevents Server-Side Request Forgery (SSRF) when parsing WSDLs from user-supplied URLs or untrusted data. The top-level WSDL fetch (e.g. SoapClient(wsdl_url=...)) is always explicit; only wsdl:import resolution inside the document is guarded.
WSDL schema validation¶
SoapApplication can validate the SOAP Body of each inbound request against the XSD types embedded in the WSDL. Validation is opt-in and disabled by default.
from soapbar import SoapApplication
soap_app = SoapApplication(
service_url="https://example.com/soap",
validate_body_schema=True, # X07 — WS-I BP 1.1 R2201
)
soap_app.register(MyService())
When enabled, the compiled lxml.etree.XMLSchema is built once — from any
WSDL-embedded <xs:schema> elements plus the schema auto-generated from the
registered services' types (exactly the schema the published WSDL advertises) —
and cached, including a computed-unavailable result. Validation runs before
deserialization, so a lexically invalid value (text in an xs:int element)
surfaces as a Client fault with the first schema error message rather than a
coercion error.
Strict mode enforces the published contract exactly: the generated schema
declares elementFormDefault="unqualified" (matching soapbar's serializer), so
a request that qualifies the wrapper's children — for example via a default
xmlns on the wrapper — is rejected even though the lenient dispatcher would
have accepted it. Only wrapped binding styles declare the global elements the
validator needs; registering a non-wrapped service on an application with
validate_body_schema=True raises ValueError instead of leaving the flag
silently inert.