В Python, как установить _soapheaders для Zeep, используя словари? - PullRequest
0 голосов
/ 15 января 2019

При работе с API-интерфейсом SOAP спецификация wsdl описывает ключ API, передаваемый в заголовке в сложной структуре с пространством имен, а также дополнительный XML без пространства имен, который относится к механизму подкачки для последовательного доступа к массовым результатам:

Спецификация

<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>

Ответ, Как установить мыльные заголовки в zeep, когда заголовок имеет несколько элементов , описывает похожий сценарий, без пространства имен «ns», но с «acm». Мне не удалось использовать этот метод.

Это работает, позволяя получить доступ к API, но без пейджера, что делает его практически бесполезным для любых методов, которые возвращают более 100 результатов:

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)

Это не работает:

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})

Ошибка : TypeError: ComplexType () получил неожиданный аргумент ключевого слова 'api_key'. Подпись: AuthenticateRequest: {api_key: xsd: строка}, пейджер: {страница: xsd: строка, per_page: xsd: строка}

Это также не работает:

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})

'пейджер' не определен в wsdl, но сервер ожидает, что он может быть там.

TypeError: ComplexType () получил неожиданный аргумент ключевого слова 'pager'. Подпись: api_key: xsd:string

Как проще всего использовать Zeep для установки пространства имен api_key и сложного элемента пейджера без пространства имен?

1 Ответ

0 голосов
/ 06 февраля 2019

Я считаю, что работать с Zeep легче, если у нас есть действительный и полный WSDL.

Простой WSDL службы API, который ожидает, что элемент без пространства имен будет импортировать схему без пространства имен, например:

<?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>

С 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>

Обратите внимание, как определение операции, которое ожидает значения заголовка, указывает на правильные сообщения:

<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: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 вашего веб-сервиса, чтобы операция описывала оба заголовка и чтобы она содержала определение схемы для обоих элементов.В примере WSDL пространство имен службы - targetNamespace="http://tempuri.org/", но оно должно указывать на URL-адрес вашей веб-службы.

Таким образом, предполагая, что ваш WSDL является действительным и полным, нам нужно определить клиента, указывающего на WSDL, а затем установитьзначения заголовка с использованием параметра _soapheaders, аналогично методу, который я использовал здесь , но построение ссылки на содержимое.Zeep может позаботиться о разных пространствах имен, но я обнаружил проблемы с пустыми:

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: Пример сгенерированного XML-запроса:

<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>

Убедитесь, что заголовок, который имеетПространство имен определяется с помощью правильного URL.

Если у вас все еще есть проблемы, это может означать, что ваш WSDL не определяет все элементы или что он не связывается правильно с внешними XSD.В этих случаях один из вариантов - сохранить локальную копию WSDL и связанных XSD, затем отредактировать файлы, чтобы исправить ссылки, а затем вместо этого указать Zeep на этот локальный файл.

...