我试图在Python 2.7上使用SOAPpy来调用使用SOAP请求的方法。该方法称为GetCursOnDate
并返回汇率。它需要一个日期参数。
我正在使用以下代码:
from SOAPpy import SOAPProxy
import datetime
date=datetime.datetime.now()
namespace ="http://web.cbr.ru/"
url = "http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx"
server = SOAPProxy(url,namespace)
print (date)
server.GetCursOnDate(date)
但是我收到了一个错误:
Fault soap:Client:Server无法识别HTTP Header SOAPAction的值:GetCursOnDate。
为什么我会收到此错误?
答案 0 :(得分:12)
默认情况下,SOAPpy使用方法名称作为HTTP SOAPAction
标头的值。如果运行以下代码,您将在调试输出中看到值:
from SOAPpy import SOAPProxy
from datetime import datetime
input = datetime.now()
namespace = "http://web.cbr.ru/"
url = "http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx"
proxy = SOAPProxy(url, namespace)
proxy.config.debug = 1
proxy.GetCursOnDate(input)
调试显示:
*** Outgoing HTTP headers ***************************
POST /DailyInfoWebServ/DailyInfo.asmx HTTP/1.0
Host: www.cbr.ru
User-agent: SOAPpy 0.12.5 (http://pywebsvcs.sf.net)
Content-type: text/xml; charset=UTF-8
Content-length: 406
SOAPAction: "GetCursOnDate"
*****************************************************
但是服务期望您可以在代理上使用附加参数设置另一个值(http://web.cbr.ru/GetCursOnDate
)。 以下代码清除错误:
from SOAPpy import SOAPProxy
from datetime import datetime
input = datetime.now()
namespace = "http://web.cbr.ru/"
url = "http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx"
soapaction = "http://web.cbr.ru/GetCursOnDate"
proxy = SOAPProxy(url, namespace = namespace, soapaction = soapaction)
proxy.config.debug = 1
proxy.GetCursOnDate(input)
调试现在将显示:
*** Outgoing HTTP headers ***************************
POST /DailyInfoWebServ/DailyInfo.asmx HTTP/1.0
Host: www.cbr.ru
User-agent: SOAPpy 0.12.5 (http://pywebsvcs.sf.net)
Content-type: text/xml; charset=UTF-8
Content-length: 406
SOAPAction: "http://web.cbr.ru/GetCursOnDate"
*****************************************************
但是虽然这个特定的错误消失了,但是这个电话无法正常工作。因为你会回答问题,我想我会给我们留言,并直接写下续集。我提到了我对Python的SOAP支持on another occasion的失望。对于这篇文章,我在这里添加所有细节作为对自己的参考,并希望作为其他用户的帮助。所以这就是......
调用无效,因为默认情况下,SOAPpy使用有序参数进行调用。它们被称为v1
,v2
,v3
等(有关详细信息,请参阅SOAPpy下载中的MethodParameterNaming.txt
文件)。您的SOAP消息将如下所示:
<SOAP-ENV:Body>
<ns1:GetCursOnDate xmlns:ns1="http://web.cbr.ru/" SOAP-ENC:root="1">
<v1>
</v1>
</ns1:GetCursOnDate>
</SOAP-ENV:Body>
此特定Web服务需要名为On_date
的参数,而不是v1
。您可以尝试使用命名参数来修复它:
from SOAPpy import SOAPProxy
from datetime import datetime
input = datetime.now()
namespace = "http://web.cbr.ru/"
url = "http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx"
soapaction = "http://web.cbr.ru/GetCursOnDate"
proxy = SOAPProxy(url, namespace = namespace, soapaction = soapaction)
proxy.config.debug = 1
proxy.GetCursOnDate(On_date = input)
您的消息现在看起来像这样:
<SOAP-ENV:Body>
<ns1:GetCursOnDate xmlns:ns1="http://web.cbr.ru/" SOAP-ENC:root="1">
<On_date>
</On_date>
</ns1:GetCursOnDate>
</SOAP-ENV:Body>
我认为日期的值缺失,因为代理有datetime
个对象的问题。我实际上没有检查是什么问题,因为此消息还有另一个问题:Web服务期望<ns1:On_date>
不是<On_date>
。
这是SOAPpy在命名空间方面存在一些问题的地方。使用原始的SOAPpy源代码,您无法更改名称空间。看来,对于大多数Python的SOAP库,您只能通过调整代码来获得所需的行为,这就是我所做的。我在一些处理名称空间和标记前缀的地方更改了SOAPBuilder.py
文件。查看原始文件here和更改后的here。
这些更改允许我使用SOAPpy Type来更好地控制消息:
from SOAPpy import SOAPProxy
from SOAPpy import Types
namespace = "http://web.cbr.ru/"
url = "http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx"
soapaction = "http://web.cbr.ru/GetCursOnDate"
input = Types.dateType(name = (namespace, "On_date"))
proxy = SOAPProxy(url, namespace = namespace, soapaction = soapaction)
proxy.config.debug = 1
proxy.GetCursOnDate(input)
现在我得到了我想要的结果:
<SOAP-ENV:Body>
<ns1:GetCursOnDate xmlns:ns1="http://web.cbr.ru/" SOAP-ENC:root="1">
<ns1:On_date xsi:type="xsd:date">2013-11-02Z</ns1:On_date>
</ns1:GetCursOnDate>
</SOAP-ENV:Body>
服务器返回上述请求的数据。
但即使上面的代码也可以改进。请注意,我在代理上为一个特定操作设置了SOAPAction
:GetCursOnDate
。如果我想在另一个操作中使用它,我需要另一个代理,或者我需要修改这个。通过使用WSDL.Proxy
,您可以从WSDL自动获取它(它提供了一个SOAPProxy
包装器,用于从Web服务的WSDL解析方法名称,名称空间和SOAPAction
。
但是,即使它自动处理SOAPAction
,它也不会获取该方法的命名空间。所以我调整了WSDL.py
文件。原始版本为here,更改的文件为here。新的客户端代码现在看起来像这样:
from SOAPpy import WSDL
from SOAPpy import Types
# you can download this and use it locally for better performance
wsdl = "http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx?wsdl"
namespace = "http://web.cbr.ru/"
input = Types.dateType(name = (namespace, "On_date"))
proxy = WSDL.Proxy(wsdl, namespace = namespace)
proxy.soapproxy.config.debug = 1
proxy.GetCursOnDate(input)
对于上面的例子,我使用了Python 2.6.6,SOAPpy 0.12.5,fpconst 0.7.2和wstools 0.4.3。对于其他人,我认为YMMV取决于您呼叫的版本或特定Web服务。总之,我还想提一下,如果你在Google上进行搜索,你会注意到大多数人推荐使用SUDS而不是SOAPpy作为SOAP客户端,所以也许看一下。祝你好运!
答案 1 :(得分:0)
看起来像targetNamespace被忽略了,但是你可以为每个操作设置一个名称空间,这对于soappy来说效果很好。
<operation name="createCall">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal" namespace="http://create.service/"/>
</input>
<output>
<soap:body use="literal" namespace="http://create.service/"/>
</output>
</operation>
当然您应该使用您的命名空间而不是http://create.service/