3

我正在尝试使用 SUDS 和 Python 与 SOAP Web 服务通信。经过大量学习 Python(是的,我是新手)并研究如何使用 SUDS 之后,我遇到了一个问题。

根据 suds,我正在调用的 Web 方法的签名是

(FWTCaseCreate){
ClassificationEventCode = None
Priority = None
Title = None
Description = None
Queue = None
DueDate = None
AssociatedObject = 
  (FWTObjectBriefDetails){
     ObjectID = 
        (FWTObjectID){
           ObjectType = None
           ObjectReference[] = <empty>
        }
     ObjectDescription = None
     Details = None
     Category = None
  }
Form = 
  (FWTCaseForm){
     FormField[] = <empty>
     FormName = None
     FormKey = None
  }
Internal = None
InteractionID = None
XCoord = None
YCoord = None
}

所以我使用 SUDS 创建我想要的类并将其发送到方法。但是我得到一个错误。所以我打开了登录,我可以看到正在发送的 XML 不正确,这导致了反序列化错误。SOAP 包如下所示

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.CRM.com/wsdl/FLTypes"    xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
   <wsse:Security>
      <wsse:BinarySecurityToken>eaadf1ddff99a8</wsse:BinarySecurityToken>
   </wsse:Security>
</SOAP-ENV:Header>
<ns1:Body>
   <ns0:FWTCaseCreate>
      <ClassificationEventCode>
         <ClassificationEventCode>2000023</ClassificationEventCode>
         <Priority>1</Priority>
         <Title>testing</Title>
         <Description>testing</Description>
         <Queue/>
         <Internal>True</Internal>
         <XCoord>356570</XCoord>
         <YCoord>168708</YCoord>
      </ClassificationEventCode>
   </ns0:FWTCaseCreate>
</ns1:Body>

如您所见,所有其他元素周围都有一个“ClassificationEventCode”元素,这不应该存在。如果我将此 xml 剪切并粘贴到 SOAPUI 中并首先删除此元素,然后将其直接发布到 Web 服务,它会成功运行。

这是我用来拨打电话的代码

client = Client(url)

#Add a header for the security
ssnns = ('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd')

ssn = Element('BinarySecurityToken', ns=ssnns).setText(binaryKey)

ssn1 = Element('Security',ns=ssnns)

ssn1.append(ssn)

client.set_options(soapheaders=ssn1) 

newCase = client.factory.create('ns1:FWTCaseCreate')

classEventCode = client.factory.create('ns1:FWTEventCode')
classEventCode.value = 2000023

newCase.ClassificationEventCode = classEventCode
newCase.Priority = 1
#optional
newCase.AssociatedObject = None
#optional
newCase.Form = None
#optional
newCase.Internal = None
#optional
newCase.InteractionID =  None
#optional
newCase.DueDate = None
#optional
newCase.Queue = None

newCase.Title = 'Title'

newCase.Description = 'description'

newCase.XCoord = '356570'

newCase.YCoord = '168708'

caseID = client.service.createCase(newCase)

有谁知道为什么会这样?我猜 SUDS 认为它应该基于 WSDL 存在。

谢谢。

4

5 回答 5

6

遇到了完全相同的问题。我的 SOAP 请求中的参数序列被包装在一个与第一个参数同名的元素中。例如

....
   <ns0:Body>
      <ns1:CreationReq>
         <ReqType>
            <ReqType>1</ReqType>
            <Title>Mr</Title>
            ....
         </ReqType>
      </ns1:CreationReq>
   </ns0:Body>
....

我已经检查了 WSDL 以确保它没有问题。

问题似乎是因为我使用 client.factory.create 方法创建了一个 CreationReq 对象。通过打印检查客户端表明我正在调用的方法没有将该对象作为参数。相反,它需要一个命名参数列表。

所以我的代码是:

req = client.factory.create('CreationReq')
req.ReqType = 1
req.Title = 'Mr'
resp = client.service.Create(req)

现在它是:

req = {}
req['ReqType'] = 1
req['Title'] = 'Mr'
resp = client.service.Create(**req)
于 2010-08-12T09:21:50.567 回答
1

如果您为 suds 服务创建客户端,您可以查看一些属性来确定需要哪些对象传递到服务调用中。

例如:

import suds
client = suds.Client(url)
for a in client.sd: #print the whole service definition
    print a

这应该向您显示前缀、带有方法的端口和类型。对于您的代码,您应该能够看到需要在服务调用中传递给 createCase 的内容。尽管 WSDL 可能将方法定义为需要“FWTCaseCreate”,但 suds 可能会选择 createCase 的定义以需要 ClassificationEventCode、Priority、Title 类型等。

因此,您不想这样做:(将 newCase 传递给第一个参数,将所有详细信息放在该标签下)

newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase)

而是像这样调用服务:(基于服务定义)

newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase.ClassificationEventCode, newCase.Priority, ...)

或许:

newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(*[getattr(newCase,a) for a in newCase.__keylist__])

传入服务调用所需的参数列表。

我不知道为什么服务调用定义被错误地解析为它是什么,但传入正确的对象并不会自动扩展为所需的正确参数列表。也许阅读 suds 源 ( http://jortel.fedorapeople.org/suds/doc/ ) 将有助于揭示答案。

于 2012-07-16T20:14:53.067 回答
0

您是将其用作配置文件还是存储信息。或者这是通过网络发送数据?

好吧,如果是这样,那么为什么不使用 json 或 json-rpc,据说它们更快、更容易解析并且更容易阅读。XML 是一种可怕的数据类型,我个人不能等到它死掉,如果您正在寻找发送数据,那么使用 json 非常值得。

于 2010-05-10T13:39:50.930 回答
0

您两次创建元素。删除这个:

classEventCode = client.factory.create('ns1:FWTEventCode') 
classEventCode.value = 2000023 

并改变这个:

newcase.ClassificationEventCode = 2000023

这应该删除那个额外的标签。

于 2010-05-12T15:32:24.587 回答
0

我发现这个线程正在寻找相同问题的解决方案。到目前为止,我研究过它只有在您将工厂创建的对象直接传递给服务方法时才会发生。并且仅适用于使用扩展(继承)的 wsdl 数据类型。

还有更多我能想到的解决方案。

  • 根本不要将工厂用于顶级类型。
  • 生成后编写 suds 插件更改 xml
  • 重写 wsdl 以不使用继承(扩展标签)
  • 在传递给服务方法之前更改对象的类型

我选择了最后一个,因为它是最简单的方法。于是就有了代码。

def sudsToDict(data):
    return dict([(str(key),val) for key,val in data])

像这样使用。

data = client.factory.create('wsdl_data_type')
# now fill with values and then
data = sudsToDict(data)
client.service.some_method(**data)
于 2014-10-02T08:57:04.763 回答