在我的应用程序中,我需要访问一个内部(公司)Soap API。到目前为止,我使用的是Zeep。但是现在访问必须通过代理,并且API的实际地址必须转换为代理的虚拟地址。
创建Zeep客户机也可以正常工作,并且我可以访问WSDL文件。然而,在调用相应的服务时出现问题,因为Zeep从WSDL文件中获取相应的URL,而这与代理的虚拟地址不匹配。
我将尝试用我的具体代码来说明下面的问题:假设SOAP API的地址是https://original-soap/wsdl?service=<service_name>
。
在代理中有一个从https://origial-soap
到http://virtual-soap
的映射。
那么Zeep应该使用的地址是http://virtual-soap/wsdl?service=<service_name>
。
我初始化我的Zeep客户端如下:
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
所以我的问题是我如何也可以改变Zeep调用服务时使用的URL,以便在这里也使用虚拟地址。
提前感谢您的帮助!
感谢@Bogdan,我使用以下代码解决了服务初始化的问题:
service = client.create_service(
client.service._binding.name, client.service._binding_options['address'].replace('https://original-soap:443', 'http://virtual-soap:80', 1)
)
还有另一种方法可以创建ServiceProxy来完成这个任务。
参见https://docs.python-zeep.org/en/master/client.html#creating-new-serviceproxy-objects
文档默认的ServiceProxy实例是使用WSDL中的地址创建的,但是上面创建ServiceProxy的方法允许对地址进行更多的控制。
{http://my-target-namespace-here}myBinding
的值是绑定标识符。如果您执行python -mzeep https://original-soap/wsdl
,您应该得到如下输出(我在这里使用一些在线示例进行演示,因为我无法访问您的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
如果您在WSDL上运行该命令时查看此操作的结果,您将看到一个" binding "部分。这就是您获取值的地方,该值很可能是Soap11Binding
的值(大多数服务只提供一个绑定,这个服务提供两个绑定,每个版本的SOAP协议一个绑定)。
对于文档中的http://my-endpoint.com/acceptance/
,您应该替换您的新SOAP地址(即您现在想要发送服务请求的新位置)。从您的示例中判断,这应该是http://virtual-soap/something
,其中something
是WSDL内部原始SOAP地址的原始路径(再次说明,由于我无法访问您的WSDL,因此您应该查看需要替换地址的哪一部分,以及哪些部分保持不变)。
至于你想调用什么操作,这是不变的。你用同样的方式称呼它。同样的方法在那里,因为你不改变服务契约,你只是改变你想要发送请求到哪里。