Within my application, I need to access an internal (corporate) Soap API. For this access I have used Zeep so far. But now the access has to go through a proxy and the actual address of the API has to be converted to a virtual address of the proxy.
Creating the Zeep client also works correctly and I can access the WSDL files. However, the problem occurs when calling the respective service, because Zeep takes the corresponding URL from the WSDL file and this does not match the virtual address of the proxy.
I'll try to illustrate the problem below with my concrete code:
Assuming the address of the SOAP API is https://original-soap/wsdl?service=<service_name>
.
In the proxy there is a mapping from https://origial-soap
to http://virtual-soap
.
So the address Zeep should use then is http://virtual-soap/wsdl?service=<service_name>
.
I initialize my Zeep client as follows:
from requests.auth import HTTPBasicAuth
from requests import Session
from zeep import Client
from zeep.transports import Transport
from zeep.helpers import serialize_object
session = Session()
session.proxies = {
'http': 'http://<proxy_host>:<proxy_port>',
'https': 'http://<proxy_host>:<proxy_port>',
}
proxy_header = {
"Proxy-Authorization": "Bearer <proxy_access_token>"
}
session.headers.update(proxy_header)
session.verify = False
session.auth = HTTPBasicAuth(<soap_user>, <soap_password>)
transport = Transport(session=session)
client = Client(wsdl='http://virtual-saop/wsdl?service=<service_name>', transport=transport)
print('CLIENT INITIALIZED') # <-- This print command is executed
soap_result = client.service['<service_function_name>'](<function parameters>) # <-- Connectivity errors occur here
So my question is how I can also change the URL that Zeep uses when calling the service so that the virtual address is used here as well.
Thanks for any help in advance!
Thanks to @Bogdan, I solved the problem using the following code for the service initialization:
service = client.create_service(
client.service._binding.name, client.service._binding_options['address'].replace('https://original-soap:443', 'http://virtual-soap:80', 1)
)
There is another way to create the ServiceProxy that's supposed to do that.
See the documentation at https://docs.python-zeep.org/en/master/client.html#creating-new-serviceproxy-objects
The default ServiceProxy instance is created using the address in the WSDL, but the above way of creating the ServiceProxy allows more control on the address.
The value for {http://my-target-namespace-here}myBinding
is your binding identifier. If you do a python -mzeep https://original-soap/wsdl
you should get an output like this one (I'm using some online example here for demonstration purposes, since I don't have access to your WSDL):
> python -mzeep http://www.dneonline.com/calculator.asmx?WSDL
Prefixes:
xsd: http://www.w3.org/2001/XMLSchema
ns0: http://tempuri.org/
Global elements:
ns0:Add(intA: xsd:int, intB: xsd:int)
ns0:AddResponse(AddResult: xsd:int)
ns0:Divide(intA: xsd:int, intB: xsd:int)
ns0:DivideResponse(DivideResult: xsd:int)
ns0:Multiply(intA: xsd:int, intB: xsd:int)
ns0:MultiplyResponse(MultiplyResult: xsd:int)
ns0:Subtract(intA: xsd:int, intB: xsd:int)
ns0:SubtractResponse(SubtractResult: xsd:int)
Global types:
xsd:anyType
xsd:ENTITIES
xsd:ENTITY
xsd:ID
xsd:IDREF
xsd:IDREFS
xsd:NCName
xsd:NMTOKEN
xsd:NMTOKENS
xsd:NOTATION
xsd:Name
xsd:QName
xsd:anySimpleType
xsd:anyURI
xsd:base64Binary
xsd:boolean
xsd:byte
xsd:date
xsd:dateTime
xsd:decimal
xsd:double
xsd:duration
xsd:float
xsd:gDay
xsd:gMonth
xsd:gMonthDay
xsd:gYear
xsd:gYearMonth
xsd:hexBinary
xsd:int
xsd:integer
xsd:language
xsd:long
xsd:negativeInteger
xsd:nonNegativeInteger
xsd:nonPositiveInteger
xsd:normalizedString
xsd:positiveInteger
xsd:short
xsd:string
xsd:time
xsd:token
xsd:unsignedByte
xsd:unsignedInt
xsd:unsignedLong
xsd:unsignedShort
Bindings:
Soap11Binding: {http://tempuri.org/}CalculatorSoap
Soap12Binding: {http://tempuri.org/}CalculatorSoap12
Service: Calculator
Port: CalculatorSoap (Soap11Binding: {http://tempuri.org/}CalculatorSoap)
Operations:
Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
Port: CalculatorSoap12 (Soap12Binding: {http://tempuri.org/}CalculatorSoap12)
Operations:
Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
If you look at the result of this when running the command on your WSDL, you will see a "Bindings" section. That's from where you get the value, which most likely will be the one for the Soap11Binding
(most services will provide just one binding, this one provides two, one for each version of the SOAP protocol).
For the http://my-endpoint.com/acceptance/
in the documentation, you should replace your new SOAP address (i.e. the new place where you want to send the service request now). Judging from your example, this should be http://virtual-soap/something
, where something
is the original path from the original SOAP address that's inside the WSDL (again, since I don't have access to you WSDL, you should look to see which part of the address you need to replace, and which stays the same).
As for what operation you want to call, that doesn't changes. You call it the same way. The same methods are there because you don't change the service contract, you just change where you want to send the request to.