如何在使用suds创建的SOAP请求中强制给属性加上命名空间前缀

2 投票
2 回答
5533 浏览
提问于 2025-04-16 15:03

我正在使用 Python 的 suds 库(版本:0.3.9 GA 构建:R659-20100219)来与一个 SOAP 服务进行交互,这个服务的结构如下:

Service ( DPWSService ) tns="http://ejb.revitalization.services.ndg/"
Prefixes (1)
ns0 = "http://ejb.revitalization.services.ndg/"
Ports (1):
(DPWSPort)
Methods (13):
addTimer(request request, )
deleteProvider(request request, )
deleteTimer(request request, )
doHarvest(request request, )
doIngest(request request, )
doNewUpdateProvider(request request, )
getHarvestHistory(GetHistoryRequest request, )
getIngestHistory(GetHistoryRequest request, )
getList(GetListType request, )
getListNames()
getProviderDetails(request request, )
getProviderStatistic(request request, )
getStatusProcess(request request, )
Types (63):
AddTimerResponse
CSWProviderDetailsType
ConfirmationType
ContactType
DataRangeType
DeleteProviderResponse
DeleteTimerResponse
DoHarvestResponse
DoIngestResponse
DoNewUpdateProviderResponse
EmailContactType
GetHarvestHistoryResponse
GetHistoryRequest
GetIngestHistoryResponse
GetListNamesResponse
GetListResponse
GetListType
GetProcessStatusResponse
GetProviderDetailsResponse
GetProviderStatisticResponse
HarvestHistoryType
HarvestProviderType
HarvestType
IngestHistoryType
ListNames
OAIProviderDetailsType
ProcessIDType
ProviderCommonType
ProviderContactType
ProviderDetail
ProviderDetailsType
ProviderIDType
ProviderStatistic
ResponseType
TimerInfoCommonType
TimerInfoDetail
TimerInfoLogDetail
addTimer
addTimerResponse
deleteProvider
deleteProviderResponse
deleteTimer
deleteTimerResponse
doHarvest
doHarvestResponse
doIngest
doIngestResponse
doNewUpdateProvider
doNewUpdateProviderResponse
getHarvestHistory
getHarvestHistoryResponse
getIngestHistory
getIngestHistoryResponse
getList
getListNames
getListNamesResponse
getListResponse
getProviderDetails
getProviderDetailsResponse
getProviderStatistic
getProviderStatisticResponse
getStatusProcess
getStatusProcessResponse

我需要发送一个 SOAP 请求,结构如下:

<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://
ejb.revitalization.services.ndg/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns0:Body>
<ns1:doIngest>
<request>
<ns1:ProcessID ns1:id="1430"/>
<ns1:EmailReportID>1031</ns1:EmailReportID>
</request>
</ns1:doIngest>
</ns0:Body>
</SOAP-ENV:Envelope>

也就是说,我需要在 id 属性前面加上目标命名空间。如果不加的话,请求就会失败 :(

我尝试了几种方法来创建我的 doIngest 请求对象,但我只能创建出如下的请求:

<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://ejb.revitalization.services.ndg/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns0:Body>
<ns1:doIngest>
<request>
<ns1:ProcessID id="1441"/>
<ns1:EmailReportID>1031</ns1:EmailReportID>
</request>
</ns1:doIngest>
</ns0:Body>
</SOAP-ENV:Envelope>

也就是说,id 属性前面没有目标命名空间的前缀。

我尝试了以下几种变体:

request = wsClient.factory.create('doIngest.request')
request.EmailReportID = "1031"
request.ProcessID = wsClient.factory.create('ProcessIDType')
request.ProcessID._id= "1430"
result=wsClient.service.doIngest(request)

还有:

request = wsClient.factory.create('{http://ejb.revitalization.services.ndg/}doIngest.request')
request.EmailReportID = "1031"
request.ProcessID = wsClient.factory.create('{http://ejb.revitalization.services.ndg/}ProcessIDType')
request.ProcessID._id="1430"
result=wsClient.service.doIngest(request)

以及:

request = wsClient.factory.create('doIngest.request')
request.EmailReportID = emailIDs
request.ProcessID = wsClient.factory.create('ProcessIDType')
request.ProcessID._id = wsClient.factory.resolver.qualify('{http://ejb.revitalization.services.ndg/}_id')
request.ProcessID._id=procID
result=wsClient.service.doIngest(request)

但我得到的 SOAP 请求都是一样的。

WSDL 文件告诉我:

<xs:complexType name="doIngest">
    <xs:sequence>
        <xs:element form="unqualified" minOccurs="0" name="request">
            <xs:complexType>
                <xs:sequence>
                    <xs:element minOccurs="0" name="ProcessID" type="tns:ProcessIDType"/>
                    <xs:element maxOccurs="unbounded" minOccurs="0" name="EmailReportID" type="xs:int"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:sequence>
</xs:complexType>

还有

<xs:complexType name="ProcessIDType">
    <xs:sequence/>
    <xs:attribute ref="tns:id" use="required"/>
    <xs:attribute ref="tns:status"/>
</xs:complexType>

这表明 id 确实需要命名空间前缀。

所以问题是,我该如何强制在我的 id 属性上加上命名空间前缀呢?

感谢 Gandi

所以解决方案是:

将 Suds 更新到 0.4(因为在版本 0.3.9 中没有 MessagePlugin)

然后:

class MyPlugin(MessagePlugin):
    def marshalled(self, context):
        ProcIDnode = context.envelope.getChild('Body').getChild('doIngest').getChild('request')[0]
        #Get the value of the id attribute  
        ProcIDattVal = ProcIDnode.get('id')
        #Remove the errant id, used as a tidy-up stage
        ProcIDnode.unset('id')
        #Get the namespace prefix for the target namespace 
        ProcIDnspref = ProcIDnode.findPrefix('http://ejb.revitalization.services.ndg/')
        #Create the new attribute name with namespace prefix applied
        newProcIDattname = ProcIDnspref + ':id'
        #Insert the new attribute.
        ProcIDnode.set(newProcIDattname,ProcIDattVal)

2 个回答

0

我不太确定最好的解决办法是什么,但在使用我的 haufe.sharepoint 模块(在 PyPI 上)与 Sharepoint 交流时,我遇到了类似的问题。最后,我对 suds 的 send() 方法进行了修改,以便根据我的需求调整命名空间。你可以在这个模块的 patches.py 文件中查看相关代码。

5

你要找的内容在 suds 的文档 中,叫做 MessagePlugin。这个 marshalled 选项可以让你在消息发送之前进行修改。你需要把它作为插件添加到你的客户端中:

self.client = Client(url, plugins=[MyPlugin()])

在 marshalled 方法中,查找 context.envelope 的子项。Python 的 vars() 函数在这里非常有用。对你来说,它应该像这样:

from suds.plugin import MessagePlugin
class MyPlugin(MessagePlugin):
    def marshalled(self, context):
        foo = context.envelope.getChild('Body').getChild...getChild(and so on)[0]
        foo.nsprefix = # or something like this

对于这个“像这样”的部分,你需要调试一下你的请求(我建议同时调试客户端和传输部分),看看你的实体的 vars。我只记得它的名字。

我上周一直在研究这个,所以可能会为你节省一些时间 :) 如果你有任何问题,随时问我,我会尽量给你更具体的帮助。

撰写回答