Hey @emir.kurklu hope all is good. Welcome to the community.
Ok now, this is an interesting topic to work on.
Let’s build everything from scratch, including a small mock server.
Server: app.py
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.middleware.wsgi import WSGIMiddleware
from spyne import Application, ServiceBase, rpc, Unicode, Integer
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
# -------- SOAP service --------
class DemoSoapService(ServiceBase):
@rpc(Unicode, _returns=Unicode)
def say_hello(ctx, name):
return f"Hello, {name}"
@rpc(Integer, Integer, _returns=Integer)
def add(ctx, a, b):
return a + b
@rpc(Unicode, _returns=Unicode)
def ping(ctx, msg):
return f"PONG:{msg}"
soap_app = Application(
[DemoSoapService],
tns="urn:example.soap.demo",
in_protocol=Soap11(validator="lxml"),
out_protocol=Soap11(),
)
soap_wsgi = WsgiApplication(soap_app)
# -------- FastAPI (REST) --------
app = FastAPI(title="SOAP + REST Demo")
# Mount SOAP under /soap (WSDL available at /soap/?wsdl)
app.mount("/soap", WSGIMiddleware(soap_wsgi))
class EchoIn(BaseModel):
anything: dict | list | str | int | float | bool | None
@app.get("/")
def root():
return {"message": "OK", "docs": "/docs", "wsdl": "/soap/?wsdl"}
@app.get("/health")
def health():
return {"status": "healthy"}
@app.post("/echo")
def echo(body: EchoIn):
return {"you_sent": body.anything}
It exposes a couple of endpoints, including SOAP and REST.
REQ: requirements.txt
fastapi==0.112.*
uvicorn[standard]==0.30.*
spyne==2.14.0
lxml>=4.9
nothing special here
- fastapi - web framework for building APIs
- uvicorn[standard] - ASGI server
- spyne - library for building SOAP services in Python
- lxml - XML parsing library required by Spyne for SOAP message handling
Dockerfile
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# Install runtime deps
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# App
COPY app.py .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
Copy, install, run.
Build
docker build -t soap-demo:latest .
This is to build our new Docker image with SOAP server
Docker compose service
soap-test-server:
image: soap-demo:latest
container_name: soap-test-server
hostname: soap-test-server
restart: unless-stopped
networks:
- n8n-infra
Adding to the docker compose in the same network as my n8n
Time to test:
Above I pinned data from calls to two of the SOAP endpoints: say_hello and add (the responses passed through XML to JSON to be more readable.
Just in case, here are the results in text:
Request to say_hello
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="urn:example.soap.demo">
<soap:Body>
<tns:say_hello>
<tns:name>emir.kurklu</tns:name>
</tns:say_hello>
</soap:Body>
</soap:Envelope>
Response:
[
{
"soap11env:Envelope": {
"xmlns:soap11env": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:tns": "urn:example.soap.demo",
"soap11env:Body": {
"tns:say_helloResponse": {
"tns:say_helloResult": "Hello, emir.kurklu"
}
}
}
}
]
Request to add:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="urn:example.soap.demo">
<soap:Body>
<tns:add>
<tns:a>17</tns:a>
<tns:b>25</tns:b>
</tns:add>
</soap:Body>
</soap:Envelope>
Response:
[
{
"soap11env:Envelope": {
"xmlns:soap11env": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:tns": "urn:example.soap.demo",
"soap11env:Body": {
"tns:addResponse": {
"tns:addResult": "42"
}
}
}
}
]
As you can see, no problem calling SOAP endpoints from HTTP Request node.