Zeep发送SOAP请求缺失必填属性引发验证错误,求解决方案
Zeep发送SOAP请求时遗漏必填属性的解决办法
问题描述
使用Zeep发送SOAP请求时,employmentRatesReplace节点缺少服务端要求的SOAP-ENC:arrayType属性,导致返回验证错误:
['Mandatory values [rate] missing in employmentRatesReplace section']
Zeep生成的问题请求结构:
<soap-env:Body> <ns0:wsdlUpdateAgreementsV2 xmlns:ns0="uri:FlexForce"> <apiKey>xxxxx</apiKey> <agreements> <UpdateAgreementV2> <extAgreementId>6005961001</extAgreementId> <employmentRatesReplace> <EmploymentRate> <fromDate>2023-08-29</fromDate> <rate>80.00</rate> </EmploymentRate> <EmploymentRate> <fromDate>2023-06-20</fromDate> <rate>70.00</rate> </EmploymentRate> <EmploymentRate> <fromDate>2023-06-19</fromDate> <rate>100.00</rate> </EmploymentRate> </employmentRatesReplace> <validationErrors/> </UpdateAgreementV2> </agreements> </ns0:wsdlUpdateAgreementsV2> </soap-env:Body>
服务端接受的正确请求结构(SoapUI测试通过):
<soap-env:Body> <ns0:wsdlUpdateAgreementsV2 xmlns:ns0="uri:FlexForce"> <apiKey>xxxx</apiKey> <agreements> <UpdateAgreementV2> <extAgreementId>6005961001</extAgreementId> <employmentRatesReplace SOAP-ENC:arrayType="tns:EmploymentRate[2]"> <item xsi:type="tns:EmploymentRate"> <fromDate xsi:type="xsd:date">2023-08-16</fromDate> <rate xsi:type="xsd:decimal">50.00</rate> </item> <item xsi:type="tns:EmploymentRate"> <fromDate xsi:type="xsd:date">2023-10-16</fromDate> <rate xsi:type="xsd:decimal">60.00</rate> </item> </employmentRatesReplace> <validationErrors/> </UpdateAgreementV2> </agreements> </ns0:wsdlUpdateAgreementsV2> </soap-env:Body>
核心差异:
- 正确结构用
<item>标签包裹每个EmploymentRate对象 employmentRatesReplace节点需要添加SOAP-ENC:arrayType="tns:EmploymentRate[N]"属性(N为数组元素数量)
解决方案
方法1:手动构造符合要求的XML节点
通过Zeep的RawElement直接生成符合服务端要求的XML结构,绕过Zeep默认序列化规则:
from zeep import Client, xsd from zeep.helpers import serialize_object from lxml import etree # 初始化Zeep客户端 client = Client('https://rc.quinyx.com/FlexForceWebServices.php?wsdl') # 构造EmploymentRate对象列表 employment_rates = [ client.get_type('tns:EmploymentRate')(fromDate='2023-08-29', rate=80.00), client.get_type('tns:EmploymentRate')(fromDate='2023-06-20', rate=70.00), client.get_type('tns:EmploymentRate')(fromDate='2023-06-19', rate=100.00) ] # 获取所需命名空间 soap_enc_ns = client.wsdl.get_namespace('SOAP-ENC') tns_ns = client.wsdl.get_namespace('tns') xsi_ns = client.wsdl.get_namespace('xsi') # 创建带SOAP-ENC属性的employmentRatesReplace节点 employment_rates_replace = etree.Element( '{uri:FlexForce}employmentRatesReplace', attrib={ f'{{{soap_enc_ns}}}arrayType': f'{tns_ns}:EmploymentRate[{len(employment_rates)}]' } ) # 为每个EmploymentRate生成item节点并添加属性 for rate in employment_rates: rate_elem = serialize_object(rate, target_cls=etree.Element) rate_elem.tag = 'item' rate_elem.set(f'{{{xsi_ns}}}type', f'{tns_ns}:EmploymentRate') employment_rates_replace.append(rate_elem) # 构造UpdateAgreementV2对象,使用RawElement传入自定义节点 update_agreement = client.get_type('tns:UpdateAgreementV2')( extAgreementId='6005961001', employmentRatesReplace=xsd.Raw(employment_rates_replace), validationErrors=None ) # 调用接口 result = client.service.wsdlUpdateAgreementsV2( apiKey='xxxxx', agreements=[update_agreement] )
方法2:使用Zeep插件自动添加属性
编写Zeep插件,在请求发送前自动为employmentRatesReplace节点添加SOAP-ENC:arrayType属性:
from zeep import Client, Plugin class AddArrayTypePlugin(Plugin): def egress(self, envelope, http_headers, operation, binding_options): # 定位employmentRatesReplace节点 nodes = envelope.xpath('//ns0:employmentRatesReplace', namespaces={'ns0': 'uri:FlexForce'}) for node in nodes: item_count = len(node.findall('.//item')) soap_enc_ns = envelope.nsmap.get('SOAP-ENC') tns_ns = envelope.nsmap.get('tns') if soap_enc_ns and tns_ns: node.set(f'{{{soap_enc_ns}}}arrayType', f'{tns_ns}:EmploymentRate[{item_count}]') return envelope, http_headers # 加载插件初始化客户端 client = Client('https://rc.quinyx.com/FlexForceWebServices.php?wsdl', plugins=[AddArrayTypePlugin()]) # 构造数据时,用item列表包裹EmploymentRate对象 employment_rates = [ {'fromDate': '2023-08-29', 'rate': 80.00}, {'fromDate': '2023-06-20', 'rate': 70.00}, {'fromDate': '2023-06-19', 'rate': 100.00} ] update_agreement = { 'extAgreementId': '6005961001', 'employmentRatesReplace': {'item': employment_rates}, 'validationErrors': None } # 调用接口 result = client.service.wsdlUpdateAgreementsV2(apiKey='xxxxx', agreements=[update_agreement])
原因说明
该服务端依赖旧版SOAP编码规范(SOAP-ENC),要求数组类型必须携带SOAP-ENC:arrayType属性,且数组元素用<item>标签包裹。而Zeep默认采用文档/文字格式序列化,不会自动生成这类SOAP-ENC相关属性和结构,因此需要手动干预或通过插件修正。
内容的提问来源于stack exchange,提问作者Frej
相关产品推荐
相关产品推荐

