在 Python 中,如何使用字典为 Zeep 设置 _soapheaders? [英] In Python, how to set _soapheaders for Zeep using Dictionaries?

查看:35
本文介绍了在 Python 中,如何使用字典为 Zeep 设置 _soapheaders?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用 SOAP api 时,wsdl 规范描述了以复杂命名空间结构以及与分页机制相关的其他非命名空间 XML 传递的 api 密钥,用于连续访问批量结果:

规格:

<soapenv:Header><ns:apiKey><api_key>***</api_key></ns:apiKey><寻呼机><page>1</page><per_page>100</per_page></寻呼机></soapenv:Header></soapenv:信封>

答案,如何当标头有多个元素时,在 zeep 中设置soap标头,描述了类似的场景,没有命名空间ns"但带有acm".我没有成功使用这种方法.

这有效,允许访问 api 但没有分页器,因此对于返回超过 100 个结果的任何方法来说,它几乎无用:

from zeep import Client, xsd# 生成头结构标头 = xsd.Element('{wsdl}AuthenticateRequest',xsd.ComplexType([xsd.Element("{wsdl}api_key", xsd.String())]))# 将值插入到标题占位符中self._header_value = header(api_key=self.api_key)

这不起作用:

from zeep import Client, xsd# 生成头结构标头 = xsd.Element('标题',xsd.ComplexType([xsd.Element('{wsdl}AuthenticateRequest',xsd.ComplexType([xsd.Element('{wsdl}api_key', xsd.String()),])),xsd.Element('寻呼机',xsd.ComplexType([xsd.Element('page', xsd.String()),xsd.Element('per_page', xsd.String()),])),]))# 错误在这里:将值插入标题占位符self._header_value = header(api_key=self.api_key, pager={'page':1,'per_page':100})

<块引用>

错误:TypeError:ComplexType() 得到了意外的关键字参数api_key".签名: AuthenticateRequest: {api_key: xsd:string}, pager: {page: xsd:string, per_page: xsd:string}

这也不起作用:

header = xsd.Element('{wsdl}AuthenticateRequest',xsd.ComplexType([xsd.Element("{wsdl}api_key", xsd.String())]),xsd.Element('寻呼机',xsd.ComplexType([xsd.Element('page', xsd.String()),xsd.Element('per_page', xsd.String()),])))# 错误在这里:将值插入标题占位符self._header_value = header(api_key=self.api_key, pager={"page":1,"per_page":100})

'pager' 未在 wsdl 中定义,但服务器希望它可以在那里.

<块引用>

TypeError: ComplexType() 得到了一个意外的关键字参数pager".签名:api_key: xsd:string

使用 Zeep 设置命名空间 api_key 和非命名空间复杂 pager 元素的最简单方法是什么?

解决方案

我发现如果我们有一个有效且完整的 WSDL,使用 Zeep 会更容易.

需要一个没有命名空间的元素的简单 API 服务 WSDL 将导入一个没有命名空间的模式,如下所示:

</s:元素><s:序列><s:element minOccurs="1" maxOccurs="1" name="ApiResult" type="s:int"/></s:sequence></s:complexType></s:元素><s:序列><s:element name="api_key" type="s:string"></s:element></s:sequence></s:complexType></s:元素></s:schema></wsdl:types><wsdl:message name="ApiSoapIn"><wsdl:part name="parameters" element="tns:Api"/></wsdl:message><wsdl:message name="ApiSoapOut"><wsdl:part name="parameters" element="tns:ApiResponse"/></wsdl:message><wsdl:message name="ApiKeyHeader"><wsdl:part name="ApiKeyHeaderParam" element="tns:apiKey"/></wsdl:message><wsdl:message name="PagerHeader"><wsdl:part name="PagerHeaderParam" ref="pager"/></wsdl:message><wsdl:portType name="ApiSoap"><wsdl:operation name="Api"><wsdl:documentation>这是一个测试 WebService.返回一个数字</wsdl:documentation><wsdl:input message="tns:ApiSoapIn"/><wsdl:output message="tns:ApiSoapOut"/></wsdl:操作></wsdl:portType><wsdl:binding name="ApiSoap" type="tns:ApiSoap"><soap:binding transport="http://schemas.xmlsoap.org/soap/http"/><wsdl:operation name="Api"><soap:operation soapAction="http://tempuri.org/Api" style="document"/><wsdl:输入><soap:header message="tns:ApiKeyHeader" part="ApiKeyHeaderParam" use="literal"/><soap:header message="tns:PagerHeader" part="PagerHeaderParam" use="literal"/><soap:body use="literal"/></wsdl:input><wsdl:输出><soap:body use="literal"/></wsdl:输出></wsdl:操作></wsdl:binding><wsdl:service name="ApiTest"><wsdl:port name="ApiSoap" binding="tns:ApiSoap"><soap:address location="http://superpc:8082/"/></wsdl:port></wsdl:service></wsdl:定义>

使用 namespaceLessElement.xsd:

<s:element name="pager"><s:序列><s:element name="page" type="s:int"></s:element><s:element name="per_page" type="s:int"></s:element></s:sequence></s:complexType></s:元素></s:schema>

注意期望标头值的操作定义如何指向正确的消息:

<soap:operation soapAction="http://tempuri.org/Api" style="document"/><wsdl:输入><soap:header message="tns:ApiKeyHeader" part="ApiKeyHeaderParam" use="literal"/><soap:header message="tns:PagerHeader" part="PagerHeaderParam" use="literal"/><soap:body use="literal"/></wsdl:input><wsdl:输出><soap:body use="literal"/></wsdl:输出></wsdl:操作>

这些依次引用正确的元素:

<wsdl:part name="ApiKeyHeaderParam" element="tns:apiKey"/></wsdl:message><wsdl:message name="PagerHeader"><wsdl:part name="PagerHeaderParam" ref="pager"/></wsdl:message>

您应该在 Web 服务的 WSDL 中检查该操作是否描述了两个标头,并且它包括两个元素的架构定义.在示例 WSDL 中,服务名称空间是 targetNamespace="http://tempuri.org/" 但这应该指向您的 Web 服务 URL.

因此假设您的 WSDL 有效且完整,我们需要定义指向 WSDL 的客户端,然后使用 _soapheaders 参数设置标头值,类似于我使用的方法 此处 但构建内容参考.Zeep 可以处理不同的命名空间,但我发现空命名空间有问题:

transport = Transport(cache=SqliteCache())self.Test = Client(wsdl='http://my-endpoint.com/production.svc?wsdl', transport=transport)# 标题对象apiKey_header = xsd.Element('{http://tempuri.org/}apiKey',xsd.ComplexType([xsd.Element('api_key', xsd.String())]))pager_header = xsd.Element('寻呼机',xsd.ComplexType([xsd.Element('页面', xsd.Integer()),xsd.Element('per_page', xsd.Integer())]))apiKey_header_value = apiKey_header( api_key=key)pager_header_value = pager_header( page=page, per_page=perpage)# 要求response = self.Test.service.Api( _soapheaders=[apiKey_header_value, pager_header_value] )logger.debug("Result={1}".format(response))# 打印:Result=2(或测试 API 发送的任何值)

生成的 XML 请求示例:

<soap-env:Header><ns0:apiKey xmlns:ns0="http://tempuri.org/"><api_key>1230011</api_key></ns0:apiKey><寻呼机><page>2</page><per_page>10</per_page></寻呼机></soap-env:Header><soap-env:Body><ns0:Api xmlns:ns0="http://tempuri.org/"/></soap-env:Body></soap-env:Envelope>

确保使用正确的 URL 定义具有命名空间的标头.

如果您仍然有问题,这可能意味着您的 WSDL 没有定义所有元素,或者它没有正确链接到外部 XSD.在这些情况下,一种选择是保存 WSDL 和链接的 XSD 的本地副本,然后编辑文件以修复引用,然后将 Zeep 指向该本地文件.

In working with a SOAP api, the wsdl spec describes the api key passed in the header in a complex namespaced structure as well as additional non-namespaced XML that relates to a paging mechanism for accessing bulk results successively:

Specification:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="https://webservice_address_here">
  <soapenv:Header>
    <ns:apiKey>
      <api_key>***</api_key>
    </ns:apiKey>
    <pager>
      <page>1</page>
      <per_page>100</per_page>
    </pager>
  </soapenv:Header>
</soapenv:Envelope>

The answer, How to set soap headers in zeep when header has multiple elements, describes a similar scenario, without the namespace "ns" but with "acm." I have not been successful in using this method.

This works, allowing access to the api but without the pager making it mostly useless for any methods that return more than 100 results:

from zeep import Client, xsd

# Generate the header structure
header = xsd.Element(
    '{wsdl}AuthenticateRequest',
    xsd.ComplexType([xsd.Element("{wsdl}api_key", xsd.String())])
)

# Insert values into header placeholders
self._header_value = header(api_key=self.api_key)

This does not work:

from zeep import Client, xsd

# Generate the header structure
header = xsd.Element(
    'Header',
    xsd.ComplexType([
        xsd.Element(
            '{wsdl}AuthenticateRequest',
            xsd.ComplexType([
                xsd.Element('{wsdl}api_key', xsd.String()),
            ])
        ),
        xsd.Element(
            'pager',
            xsd.ComplexType([
                xsd.Element('page', xsd.String()),
                xsd.Element('per_page', xsd.String()),
            ])
        ),
    ])
)

# ERROR HERE: Insert values into header placeholders
self._header_value = header(api_key=self.api_key, pager={'page':1,'per_page':100})

Error: TypeError: ComplexType() got an unexpected keyword argument 'api_key'. Signature: AuthenticateRequest: {api_key: xsd:string}, pager: {page: xsd:string, per_page: xsd:string}

This also does not work:

header = xsd.Element(
    '{wsdl}AuthenticateRequest',
    xsd.ComplexType([xsd.Element("{wsdl}api_key", xsd.String())]),
    xsd.Element(
        'pager',
        xsd.ComplexType([
            xsd.Element('page', xsd.String()),
            xsd.Element('per_page', xsd.String()),
        ])
    )
)

# ERROR HERE: Insert values into header placeholders
self._header_value = header(api_key=self.api_key, pager={"page":1,"per_page":100})

'pager' is not defined in the wsdl but the server expects that it could be there.

TypeError: ComplexType() got an unexpected keyword argument 'pager'. Signature: api_key: xsd:string

What is the simplest way using Zeep to set the namespace api_key and non-namespaced complex pager element?

解决方案

I find it's easier to work with Zeep if we have a valid and complete WSDL.

A simple API service WSDL that expects an element without namespace would import a schema with no namespace like this:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" >
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
      <s:import schemaLocation="namespaceLessElement.xsd"/>
      <s:element name="Api" minOccurs="0" maxOccurs="1">
      </s:element>
      <s:element name="ApiResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="ApiResult" type="s:int"/>
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="apiKey">
        <s:complexType>
          <s:sequence>
            <s:element name="api_key" type="s:string"></s:element>
          </s:sequence>
        </s:complexType>
      </s:element>
    </s:schema>
  </wsdl:types>
  <wsdl:message name="ApiSoapIn">
    <wsdl:part name="parameters" element="tns:Api"/>
  </wsdl:message>
  <wsdl:message name="ApiSoapOut">
    <wsdl:part name="parameters" element="tns:ApiResponse"/>
  </wsdl:message>
  <wsdl:message name="ApiKeyHeader">
    <wsdl:part name="ApiKeyHeaderParam" element="tns:apiKey"/>
  </wsdl:message>
  <wsdl:message name="PagerHeader">
    <wsdl:part name="PagerHeaderParam" ref="pager"/>
  </wsdl:message>
  <wsdl:portType name="ApiSoap">
    <wsdl:operation name="Api">
      <wsdl:documentation>This is a test WebService. Returns a number</wsdl:documentation>
      <wsdl:input message="tns:ApiSoapIn"/>
      <wsdl:output message="tns:ApiSoapOut"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="ApiSoap" type="tns:ApiSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="Api">
      <soap:operation soapAction="http://tempuri.org/Api" style="document"/>
      <wsdl:input>
        <soap:header message="tns:ApiKeyHeader" part="ApiKeyHeaderParam" use="literal"/>
        <soap:header message="tns:PagerHeader" part="PagerHeaderParam" use="literal"/>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="ApiTest">
    <wsdl:port name="ApiSoap" binding="tns:ApiSoap">
      <soap:address location="http://superpc:8082/"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

With namespaceLessElement.xsd:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified">
  <s:element name="pager">
    <s:complexType>
      <s:sequence>
        <s:element name="page" type="s:int"></s:element>
        <s:element name="per_page" type="s:int"></s:element>
      </s:sequence>
    </s:complexType>
  </s:element>
</s:schema>

Note how the operation definition that expects header values points to correct messages:

<wsdl:operation name="Api">
  <soap:operation soapAction="http://tempuri.org/Api" style="document"/>
  <wsdl:input>
    <soap:header message="tns:ApiKeyHeader" part="ApiKeyHeaderParam" use="literal"/>
    <soap:header message="tns:PagerHeader" part="PagerHeaderParam" use="literal"/>
    <soap:body use="literal"/>
  </wsdl:input>
  <wsdl:output>
    <soap:body use="literal"/>
  </wsdl:output>
</wsdl:operation>

and these in turn reference correct elements:

<wsdl:message name="ApiKeyHeader">
  <wsdl:part name="ApiKeyHeaderParam" element="tns:apiKey"/>
</wsdl:message>
<wsdl:message name="PagerHeader">
  <wsdl:part name="PagerHeaderParam" ref="pager"/>
</wsdl:message>

You should check in the WSDL of your web service that the operation describes both headers and that it includes a schema definition for both elements. In the example WSDL the service namespace is targetNamespace="http://tempuri.org/" but this should point to your web service URL.

So assuming your WSDL is valid and complete, we need to define the Client pointing to the WSDL and then set the header values using the _soapheaders parameter, similar to the method I used here but building the content reference. Zeep can take care of the different namespaces but I found issues with empty ones:

transport = Transport(cache=SqliteCache())
self.Test = Client(wsdl='http://my-endpoint.com/production.svc?wsdl', transport=transport)

# Header objects
apiKey_header = xsd.Element(
    '{http://tempuri.org/}apiKey',
    xsd.ComplexType([
        xsd.Element(
            'api_key', xsd.String()
        )
    ])
)

pager_header = xsd.Element(
    'pager',
    xsd.ComplexType([
        xsd.Element(
            'page', xsd.Integer()
        ),
        xsd.Element(
            'per_page', xsd.Integer()
        )
    ])
)

apiKey_header_value = apiKey_header( api_key=key)
pager_header_value = pager_header( page=page, per_page=perpage)

# Request
response = self.Test.service.Api( _soapheaders=[apiKey_header_value, pager_header_value] )

logger.debug("Result={1}".format(response))

# Prints: Result=2 (or whatever value the test API sends)

EDIT: Example of generated XML request:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
   <soap-env:Header>
      <ns0:apiKey xmlns:ns0="http://tempuri.org/">
         <api_key>1230011</api_key>
      </ns0:apiKey>
      <pager>
         <page>2</page>
         <per_page>10</per_page>
      </pager>
   </soap-env:Header>
   <soap-env:Body>
      <ns0:Api xmlns:ns0="http://tempuri.org/"/>
   </soap-env:Body>
</soap-env:Envelope>

Make sure that the header that has a namespace is defined with the correct URL.

If you still have problems it may mean your WSDL does not define all elements or that it's not linking correctly to external XSDs. In those cases one option is to save a local copy os the WSDL and linked XSDs, then edit the files to fix references and then point Zeep to that local file instead.

这篇关于在 Python 中,如何使用字典为 Zeep 设置 _soapheaders?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆