如何使用 SOAPpy 发出 SOAP 请求



我正在尝试通过在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)

但是我得到一个错误:

错误 soap:客户端:服务器无法识别 HTTP 标头 SOAPAction: GetCursOnDate 的值。

为什么我会收到此错误?

默认情况下,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支持的失望。对于这篇文章,我在这里添加所有细节作为对自己的参考,并希望对其他用户有所帮助。所以它来了...

调用将不起作用,因为默认情况下 SOAPpy 使用有序参数进行调用。它们被称为v1v2v3等(有关更多详细信息,请参阅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文件。在此处查看原始文件,在此处查看更改的文件。

这些更改允许我使用 SOAPpy 类型对消息进行更精细的控制:

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>

服务器返回上述请求的数据。

但即使是上面的代码也可以改进。请注意,我正在为一个特定操作设置代理SOAPActionGetCursOnDate 。如果我想将其与另一个操作一起使用,我需要另一个代理,或者我需要修改这个代理。通过使用WSDL.Proxy,您可以从 WSDL 自动获取此信息(它提供了一个 SOAPProxy 包装器,用于解析 Web 服务的 WSDL 中的方法名称、命名空间和SOAPAction)。

但即使这会自动处理SOAPAction,它也不会选取该方法的命名空间。所以我调整了WSDL.py文件。原始版本在这里,更改的文件在这里。新的客户端代码现在如下所示:

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服务。总之,我还想提一下,如果你在谷歌上搜索,你会发现大多数人推荐SUDS而不是SOAPpy作为SOAPpy作为SOAP客户端,所以也许也看看它。祝你好运!

看起来 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/

相关内容

  • 没有找到相关文章

最新更新