Недавно мы начали использовать Spyne, и он кажется очень мощным. В нашем случае нам нужно воспроизвести устаревший SOAP API, который мы заменяем. Чтобы обеспечить совместимость с текущими клиентами, мы хотим, чтобы WSDL, сгенерированный spyne, максимально соответствовал текущему WSDL.
Как было отмечено в этом вопросе SO:
Как я могу запретить Spyne обертывать аргументы в complexType?
Spyne оборачивает входящие параметры для методов с несколькими простыми параметрами в структуру ComplexType.
В прежних версиях wsdl, эта операция
<operation name="UCDprovision_password" parameterOrder="p_msisdn p_pin p_password">
<input name="UCDprovision_passwordRequest" message="impl:UCDprovision_passwordRequest"/>
<output name="UCDprovision_passwordResponse" message="impl:UCDprovision_passwordResponse"/>
</operation>
связана с этим сообщением:
<message name="UCDprovision_passwordRequest">
<part name="p_msisdn" type="xsd:string"/>
<part name="p_pin" type="xsd:string"/>
<part name="p_password" type="xsd:string"/>
</message>
и этим ComplexType:
<complexType name="ObjResultadoBasico">
<sequence>
<element name="PError" type="xsd:int"/>
<element name="SError" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
Мы создали следующий шпионский код для репликации операция:
class ObjResultadoBasico(ComplexModel):
PError = Integer
SError = String
class UCDService(ServiceBase):
@rpc(String, String, String, _returns=ObjResultadoBasico, _in_message_name='UCDprovision_passwordRequest')
def UCDprovision_password(ctx,p_msisdn,p_pin,p_password):
return ObjResultadoBasico('1',p_msisdn)
И это дает следующий WSDL (нерелевантные части опущены)
<xs:complexType name="ObjResultadoBasico">
<xs:sequence>
<xs:element name="PError" type="xs:integer" minOccurs="0" nillable="true"/>
<xs:element name="SError" type="xs:string" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="UCDprovision_passwordRequest">
<xs:sequence>
<xs:element name="p_msisdn" type="xs:string" minOccurs="0" nillable="true"/>
<xs:element name="p_pin" type="xs:string" minOccurs="0" nillable="true"/>
<xs:element name="p_password" type="xs:string" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>
<wsdl:operation name="UCDprovision_password" parameterOrder="UCDprovision_passwordRequest">
<wsdl:input name="UCDprovision_passwordRequest" message="tns:UCDprovision_passwordRequest"/>
<wsdl:output name="UCDprovision_passwordResponse" message="tns:UCDprovision_passwordResponse"/></wsdl:operation>
Разница, над которой мы пытаемся работать, - это ComplexType, оборачивающий UCDprovision_passwordRequest, который был подробно описан в устаревшем WSDL.
РЕДАКТИРОВАТЬ: добавление вывода, когда сообщение запроса предварительно определено в собственном сложном типе, и поэтому сама операция может использовать «голый» стиль, используя код, предложенный Бурак Арслан ниже:
<xs:complexType name="UCDprovision_passwordRequest">
<xs:sequence>
<xs:element name="p_msisdn" type="xs:string" minOccurs="0" nillable="true"/>
<xs:element name="p_pin" type="xs:string" minOccurs="0" nillable="true"/>
<xs:element name="p_password" type="xs:string" minOccurs="0" nillable="true"/>
</xs:sequence>
</xs:complexType>
<xs:element name="UCDprovision_password" type="tns:UCDprovision_passwordRequest"/>
<wsdl:message name="UCDprovision_password">
<wsdl:part name="UCDprovision_password" element="tns:UCDprovision_password"/>
</wsdl:message>
Проблема с этим решением заключается в том, что я не хочу заключать в оболочку не саму операцию, а входное сообщение для этой операции . Я предполагаю, что это результат, потому что входное сообщение UCDprovision_passwordRequest объявлено как ComplexModel.
Используя SOAPUI, это примеры запросов:
Старый WSDL
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ucd="http://UCD">
<soapenv:Header/>
<soapenv:Body>
<ucd:UCDprovision_pin>
<p_msisdn>?</p_msisdn>
<p_usuario>?</p_usuario>
<p_nemonico>?</p_nemonico>
<p_pin>?</p_pin>
</ucd:UCDprovision_pin>
</soapenv:Body>
</soapenv:Envelope>
Spyne WSDL
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ucd="UCD">
<soapenv:Header/>
<soapenv:Body>
<ucd:UCDprovision_pin>
<ucd:p_msisdn>?</ucd:p_msisdn>
<ucd:p_usuario>?</ucd:p_usuario>
<ucd:p_nemonico>?</ucd:p_nemonico>
<ucd:p_pin>?</ucd:p_pin>
</ucd:UCDprovision_pin>
</soapenv:Body>
</soapenv:Envelope>
В конце концов, как вы видите, с точки зрения клиента отличается только пространство имен параметров, и это, вероятно, поправимо (я не не пробовал, как бы вы это сделали?).
Поскольку нам просто нужно принять то, что клиент отправляет со старым WSDL, я протестировал «мягкую» проверку и запросил со старым WSDL принимается кодом Spyne (который генерирует новый WSDL).
Мы продолжим тестирование, но я думаю, что в конце, если это не рекомендуется, я просто приму определения ComplexType и буду использовать «мягкая» валидация, которая решает нашу проблему. Единственный вопрос, как создать WSDL без пространства имен в атрибутах ComplexType
РЕДАКТИРОВАТЬ: следующий текст не имеет значения, если патч не требуется.
Как указано в справочном вопросе выше, если бы у нас был только один in_parameter, мы могли бы использовать _body_type = 'bare' в rp c декоратор, чтобы избежать этого, но это не разрешено в spyne для 0 или более чем 1 параметра. Я уверен, что для этого, конечно, есть веские причины.
Все сказанное, и с целью максимально возможной репликации исходного WSDL мы хотели бы разработать патч, чтобы добавить опцию для этот мультипараметр должен быть представлен как в WSDL. Если возможно, мы также хотели бы внести этот патч в апстрим, если у нас будет достаточно хорошая реализация.
А теперь вопрос: как лучше всего подойти к этому патчу?
В первом обзоре код, в котором создается этот сегмент XML, выглядит следующим образом: в decorator.py:
message = None
if body_style_str == 'bare':
if len(in_params) > 1:
raise LogicError("body_style='bare' can handle at most one "
"function argument.")
if len(in_params) == 0:
message = ComplexModel.produce(type_name=in_message_name,
namespace=ns, members=in_params)
else:
message, = in_params.values()
message = message.customize(sub_name=in_message_name, sub_ns=ns)
if issubclass(message, ComplexModelBase) and not message._type_info:
raise LogicError("body_style='bare' does not allow empty "
"model as param")
# there can't be multiple arguments here.
if message.__type_name__ is ModelBase.Empty:
message._fill_empty_type_name(ns, in_message_name,
"%s_arg0" % in_message_name)
else:
message = ComplexModel.produce(type_name=in_message_name,
namespace=ns, members=in_params)
message.__namespace__ = ns
Достаточно ли нам добавить дополнительный флаг в декоратор и использовать его? чтобы создать объект сообщения так, как мы хотим, вместо того, чтобы идти по маршруту ComplexType? А ля:
if len(in_params) > 1:
if (parameters_as_message):
message = message.special_way_to_construct(type_name=in_message_name,
namespace=ns, members=in_params)
else:
raise LogicError("body_style='bare' can handle at most one "
"function argument.")
Спасибо, что дочитали до этого места, и если Бурак Арслан прочитает это, спасибо за всю вашу работу!