如何在使用suds创建的SOAP请求中强制给属性加上命名空间前缀
我正在使用 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 个回答
我不太确定最好的解决办法是什么,但在使用我的 haufe.sharepoint 模块(在 PyPI 上)与 Sharepoint 交流时,我遇到了类似的问题。最后,我对 suds 的 send() 方法进行了修改,以便根据我的需求调整命名空间。你可以在这个模块的 patches.py 文件中查看相关代码。
你要找的内容在 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。我只记得它的名字。
我上周一直在研究这个,所以可能会为你节省一些时间 :) 如果你有任何问题,随时问我,我会尽量给你更具体的帮助。