wsfactory - spyne application manager¶
wsfactory - это django-приложение, которое предоставляет гибкое управление сервисами spyne в проекте django.
Contents:
Installation and QuickStart guide¶
Установка¶
Чтобы установить приложение достаточно выполнить в окружении:
pip install wsfactory -i http://pypi.bars-open.ru/simple
Вместе с приложением будут установлены следующие зависимости:
- lxml
- spyne
Добавление приложения в проект¶
Как и все обычные django приложения, нужно просто добавить wsfactory в список установленных приложений и расширить urlpatterns:
# где-то в settings.py
INSTALLED_APPS += "wsfactory",
# где-то в urls.py
import wsfactory.urls
urlpatterns += wsfactory.urls.urlpatterns
Теперь необходимо с конфигурировать wsfactory. Сначала скопируем болванку для конфига, выполним менедж-комманду:
cd path/to/directory/you/place/config/
django_admin.py wsfactory_default_config
После чего в директории, в которой выполнялась команда появится файл wsfactory_config.xml
примерно следующего содержания:
<WSConfig xmlns="http://bars-open.ru/schema/wsfactory"
ApplicationClass="spyne.application.Application"
WsgiClass="spyne.server.django.DjangoApplication"
ServiceClass="spyne.service.ServiceBase"
ApiHandler="wsfactory.views.api_handler">
<Protocols>
<!-- Протоколы -->
<Protocol code="soap11" direction="BOTH" module="spyne.protocol.soap.soap11.Soap11" name="SOAP 1.1"/>
<Protocol code="json" direction="BOTH" module="spyne.protocol.json.JsonDocument" name="JSON Protocol"/>
<Protocol code="http-rpc" direction="IN" module="spyne.protocol.http.HttpRpc" name="HTTP RPC"/>
<!-- установите модуль spyne-smev и раскомментируйте следующие строки, чтобы добавить поддежку протоколв СМЭВ -->
<Protocol code="soap11wsse" direction="BOTH" module="spyne_smev.wsse.Soap11WSSE" name="SOAP 1.1 WSSE"/>
<Protocol code="smev256" direction="BOTH" module="spyne_smev.smev256.Smev256" name="СМЭВ 2.5.6"/>
</Protocols>
<ApiRegistry>
<!-- API-методы -->
<Api code="Code" module="some.module.ApiFunction" name="Do some action"/>
</ApiRegistry>
<Services>
<!-- Сервисы, они же услуги - наборы методов-API -->
<Service code="Service" name="Sample service">
<Api code="Code"/>
</Service>
</Services>
<SecurityProfile>
<!-- Профили безопасности WS-Security. Требует установки модуля spyne-smev -->
<Modules>
<!-- Описание модулей -->
<Module code="x509token" path="spyne_smev.wsse.protocols.X509TokenProfile" name="X509 Token Profile"/>
</Modules>
<Security module="x509token" code="security" name="Default security profile">
<!-- Декларация объектов профилей безопасности -->
<Param key="certificate" valueType="string">path_to_certificate_file</Param>
<Param key="private_key" valueType="string">path_to_pkey_file</Param>
<Param key="private_key_pass" valueType="string">pkey_password</Param>
</Security>
</SecurityProfile>
<Applications>
<!-- Реестр веб-сервисов (Соответствия протоколов-сервисов) -->
<Application name="SampleApp" service="Service">
<InProtocol code="soap11"/>
<OutProtocol code="soap11"/>
</Application>
</Applications>
</WSConfig>
Пишем код¶
Подробно файл конфигурации, разберем позже, а пока создадим в нашем проекте один простой сервис:
# гдето в проекте файл project/service.py
from itertools import repeat
from spyne import srpc, Integer, Unicode, Iterable
@srpc(Unicode, Integer, _returns=Iterable(Unicode))
def SayHello(name, times):
return repeat(u"Hello, {0}!".format(name), times)
Декларируем веб-сервис¶
После чего отредактируем файл wsfactory_config.xml
. Сперва добавим api-метод
в соответствующую секцию - в теге ApiRegistry:
<ApiRegistry>
<Api code="SayHello" module="project.service.SayHello" name="Приветствие"/>
</ApiRegistry>
Где атрибут code
- это уникальный идентификатор метода, module
- путь, по
которому можно импортировать этот метод, name
- человечное название метода.
Все атрибуты являются обязательными.
Tip
Для редактирования xml лучше использовать IDE или редактор с поддержкой xsd-схем,
т.к. в данном случае, будет автокомплит и проверка типов. xsd-схема
конфигурации лежит в пакете wsfactory
, и если файл конфигурации был создан
менедж-коммандой, то она будет подключена к файлу конфигурации через xsi:schemaLocation.
Дальше задекларируем сервис - то есть набор api-методов:
<Services>
<Service code="SayHelloService" name="Say Hello Service">
<Api code="SayHello"/>
</Service>
</Services
Здесь атрибут code
также является идентификатором, а name
человечным названием.
В элементе Service
обязательно должен содержаться хотя бы один элемент Api
, у которого в
атрибуте code
указывается ссылка на api-метод из элемента ApiRegistry
.
Теперь осталось задекларировать сам веб-сервис:
<Applications>
<Application name="SayHelloService" service="SayHelloService" tns="http://company.domain/tns">
<InProtocol code="http-rpc"/>
<OutProtocol code="json"/>
</Application>
</Applications>
Здесь атрибут name
является идентификатором веб-сервиса, атрибут service
ссылкой на сервис описанный в елементе Services
, a tns
- это неймспейс,
который будет использоваться для wsdl-документа.
В элементах InProtocol
и OutProtocol
в атрибутах code
указывается
ссылка на протоколы, описанные в элементе Protocols
. В нашем примере мы
выбрали spyne-протоколы HttpRpc на входе и JsonDocument на выходе.
Note
Тут же можно указать параметры инициализации протоколов, а так же профиль безопасности ws-security, но об этом будет сказано дальше.
В итоге должен получиться такой xml-документ:
<WSConfig xmlns="http://bars-open.ru/schema/wsfactory"
ApplicationClass="spyne.application.Application"
WsgiClass="spyne.server.django.DjangoApplication"
ServiceClass="spyne.service.ServiceBase"
ApiHandler="wsfactory.views.api_handler">
<Protocols>
<!-- Протоколы -->
<Protocol code="soap11" direction="BOTH" module="spyne.protocol.soap.soap11.Soap11" name="SOAP 1.1"/>
<Protocol code="json" direction="BOTH" module="spyne.protocol.json.JsonDocument" name="JSON Protocol"/>
<Protocol code="http-rpc" direction="IN" module="spyne.protocol.http.HttpRpc" name="HTTP RPC"/>
<!-- установите модуль spyne-smev и раскомментируйте следующие строки, чтобы добавить поддежку протоколв СМЭВ -->
<Protocol code="soap11wsse" direction="BOTH" module="spyne_smev.wsse.Soap11WSSE" name="SOAP 1.1 WSSE"/>
<Protocol code="smev256" direction="BOTH" module="spyne_smev.smev256.Smev256" name="СМЭВ 2.5.6"/>
</Protocols>
<ApiRegistry>
<!-- API-методы -->
<Api code="SayHello" module="project.service.SayHello" name="Приветствие"/>
</ApiRegistry>
<Services>
<!-- Сервисы, они же услуги - наборы методов-API -->
<Service code="SayHelloService" name="Say Hello Service">
<Api code="SayHello"/>
</Service>
</Services>
<SecurityProfile>
<!-- Профили безопасности WS-Security. Требует установки модуля spyne-smev -->
<Modules>
<!-- Описание модулей -->
<Module code="x509token" path="spyne_smev.wsse.protocols.X509TokenProfile" name="X509 Token Profile"/>
</Modules>
<Security module="x509token" code="security" name="Default security profile">
<!-- Декларация объектов профилей безопасности -->
<Param key="certificate" valueType="string">path_to_certificate_file</Param>
<Param key="private_key" valueType="string">path_to_pkey_file</Param>
<Param key="private_key_pass" valueType="string">pkey_password</Param>
</Security>
</SecurityProfile>
<Applications>
<!-- Реестр веб-сервисов (Соответствия протоколов-сервисов) -->
<Application name="SayHelloService" service="SayHelloService">
<InProtocol code="http-rpc"/>
<OutProtocol code="json"/>
</Application>
</WSConfig>
Подключение файла конфигурации¶
Теперь осталось подключить файл конфигурации, для этого гдето в settings.py добавим:
WSFACTORY_CONFIG_FILE = "/path/to/config/file/wsfactory_config.xml"
Проверяем¶
Запустим dev-сервер и наш сервис станет доступен по урлу:
http://localhost:8000/wsfactory/api/SayHelloService
Выполним запрос:
curl "http://localhost:8000/wsfactory/api/SayHelloService/SayHello?name=Tim×=4 | python -m json.tool
В результате получим:
[
"Hello, Tim!",
"Hello, Tim!",
"Hello, Tim!",
"Hello, Tim!"
]
Допустим нам понадобилось чтобы, этот же сервис отдавал данные по спецификации Soap 1.1.
Все что нам нужно - это просто добавить новый Application
в конфигурацию:
<Applications>
<!-- Реестр веб-сервисов (Соответствия протоколов-сервисов) -->
<Application name="SayHelloService" service="SayHelloService">
<InProtocol code="http-rpc"/>
<OutProtocol code="json"/>
</Application>
<Application name="SayHelloSoap" service="SayHelloSercice">
<InProtocol code="http-rpc"/>
<OutProtocol code="soap11"/>
</Application>
</Applications>
Чтобы новая версия конфигурации применилась необходимо либо перезапустить сервер, либо выполнить менедж-комманду:
django_admin.py wsfactory_reload
Ещё раз повторим запрос, немного изменив урл:
curl "http://localhost:8000/wsfactory/api/SayHelloSoap/SayHello?name=Tim×=4 | python -m json.tool
И получим результат:
<senv:Envelope xmlns:tns="http://company.domain/tns" xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:SayHelloResponse>
<tns:SayHelloResult>
<tns:string>Hello, Tim!</tns:string>
<tns:string>Hello, Tim!</tns:string>
<tns:string>Hello, Tim!</tns:string>
<tns:string>Hello, Tim!</tns:string>
</tns:SayHelloResult>
</tns:SayHelloResponse>
</senv:Body>
</senv:Envelope>
Немного модифицируем наш api-метод:
@srpc(Unicode, Integer,
_returns=Iterable(Unicode, member_name="Greeting"),
_out_variable_name="Greetings")
def SayHello(name, times):
return repeat(u"Hello, {0}!".format(name), times)
Перезапустим сервер, поновой выполним запрос и получим результат:
<senv:Envelope xmlns:tns="http://company.domain/tns" xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:SayHelloResponse>
<tns:Greetings>
<tns:Greeting>Hello, Tim!</tns:Greeting>
<tns:Greeting>Hello, Tim!</tns:Greeting>
<tns:Greeting>Hello, Tim!</tns:Greeting>
<tns:Greeting>Hello, Tim!</tns:Greeting>
</tns:Greetings>
</tns:SayHelloResponse>
</senv:Body>
</senv:Envelope>
Таким образом, мы написали в коде api-метод, а сам веб-сервис описали декларативно в конфигурации.
Spyne¶
Как обычно, пишутся веб-сервисы на spyne?
Мы создаем сервис, наследуясь от класса ServiceBase, и реализуем в нём методы нашего api.
Потом нужные нам методы заворачиваем в декораторы rpc
или srpc
:
class Service(ServiceBase):
@srpc(_returns=Datetime):
def GetNow():
return datetime.datetime.now()
@srpc(Unicode, _returns=Unicode)
def Echo(string):
return string
Потом созданный сервис связывается с протоколами через Application
, который
далее передается в wsgi-приложение:
app = Application(
[Service], "tns", "name",
in_protocol=HttpRpc(),
out_protocol=JsonDocument())
wsgi_app = DjangoApplication(app)
Разберем, что тут означает каждая переменную:
class Service
- spyne-сервис, набор api-методов. Для удобства далее будем называть их просто сервисами или “услуга”def GetNow
- метод сервиса, или api-методapp
- spyne-приложение, это некий клей, который связывает между собой, сервисы и протоколыwsgi_app
- wsgi-приложение, непосредственно обработчик запросов
Связку wsgi-приложения и spyne-приложения, для простоты, будет называть - “веб-сервис”.
Используя wsfactory, вам необходимо только описать каждый api-метод отдельно
главное, чтобы их можно было импортировать через importlib.import_module
.:
# где-то в проекте, например в project/api.py
@srpc(_returns=Datetime):
def GetNow():
return datetime.datetime.now()
@srpc(Unicode, _returns=Unicode)
def Echo(string):
return string
Далее нужно перечислить их в файле конфигурации. Протоколы, услуги и веб-сервисы декларируются в файле конфигурации.
Configuration file¶
WSConfig¶
Корневой элемент конфигурации - WSConfig
, с неймспейсом http://bars-open.ru/schema/wsfactory.
Этот элемент может содержать необязательные атрибуты, в которых можно перегрузить классы spyne:
Attribute | Description | Default value |
---|---|---|
ApplicationClass |
Класс spyne-приложения | spyne.application.Application |
WsgiClass |
Класс wsgi-приложения | spyne.server.django.WsgiApplication |
ServiceClass |
Класс сервиса | spyne.service.ServiceBase |
ApiHandler |
django view для обработки запросов | wsfactory.views.api_handler |
Таким образом, если в проекте требуется перегрузить spyne-классы, то подключить их можно в этих атрибутах.
ApiRegistry¶
Todo
добавить текст сюда
Protocols¶
Все протоколы, которые планируется использовать в проекте, необходимо перечислить
в конфигурации в элементе Protocols
. Каждому протоколу соответствует один
элемент Protocol
, с обязательными атрибутами code
, name
, module
.
code
- уникальный идентификатор (обычно, просто строка, но допускаются и числа)name
- человечное название протокола (любой юникод)module
- путь к модулю + имя класса протокола с точкой в качестве разделителя (например, “spyne.protocol.http.HttpRpc”)
Протоколы - это обычные python классы, и они принимают аргументы в конструкторе.
wsfactory позволяет задать параметры для протокола, которые будут переданы
как keyword-аргументы в конструктор. Для этого нужно добавить в элемент Protocol
элемент Param
, с обязательными аргументами key
, valueType
:
key
- имя параметра, (например validator)valueType
- тип параметра, допустимые значения: “string”, “int”, “float”, “bool”, “password”, “text”
Так же тут можно указать два необязательных аргумента name
и required
,
которые используются в приложении m3_wsfatory, где:
name
- человечное имя параметраrequired
- “true” или “false”, указываем является-ли атрибут обязательным
Значение параметра записывает в тексте в нутри элемента Param
.
Attention
Параметры описанные внутри элемента Protocol
, будут применены ко всем
веб-сервисам, которые используют этот протокол.
Пример:
<Protocols>
<!-- Протоколы -->
<Protocol code="soap11" direction="BOTH" module="spyne.protocol.soap.soap11.Soap11" name="SOAP 1.1">
<Param key="validator" valueType="string">lxml</Param>
<Param key="pretty_print" valueType="bool">true</Param>
</Protocol>
<Protocol code="json" direction="BOTH" module="spyne.protocol.json.JsonDocument" name="JSON Protocol"/>
<Protocol code="http-rpc" direction="IN" module="spyne.protocol.http.HttpRpc" name="HTTP RPC"/>
</Protocols>
Services¶
Todo
добавить текст сюда
Soap WS Security¶
Note
Данный функционал требует установки модуля spyne-smev
В элементе SecurityProfile
настраиваются профили безопасности SOAP WS Security.
Впервую очередь необходимо описать модули безопасности в элементах Module
:
<SecurityProfile>
<!-- Профили безопасности WS-Security. Требует установки модуля spyne-smev -->
<Modules>
<!-- Описание модулей -->
<Module code="x509token" path="spyne_smev.wsse.protocols.X509TokenProfile" name="X509 Token Profile"/>
</Modules>
</SecurityProfile>
Атрибуты Module
:
Атрибут | Описание | Обязательный |
---|---|---|
code | идентификатор модуля безопасности | да |
path | путь, по которому его можно импортировать | да |
name | человечье название | да |
Модули безопасности - это классы наследники класса spyne_smev.wsse.protocols.BaseWSS.
Внути элемента Module
, по аналогии с Protocol
, можно указать параметры по умолчанию:
<Module code="x509token" path="spyne_smev.wsse.protocols.X509TokenProfile" name="X509 Token Profile">
<Param key="private_key_path" valueType="string">/path/to/private_key</Param>
<Param key="certificate_path" valueType="string">/path/to/certificate</Param>
<Param key="private_key_pass" valueType="password">P@ssw0rd</Param>
</Module>
Далее декларируем профили в элементах Security
:
<SecurityProfile>
<!-- Профили безопасности WS-Security. Требует установки модуля spyne-smev -->
<Modules>
<!-- Описание модулей -->
<Module code="x509token" path="spyne_smev.wsse.protocols.X509TokenProfile" name="X509 Token Profile"/>
</Modules>
<Security module="x509token" code="security" name="Default security profile">
<Param key="certificate" valueType="string">path_to_certificate_file</Param>
<Param key="private_key" valueType="string">path_to_pkey_file</Param>
<Param key="private_key_pass" valueType="string">password</Param>
</Security>
<Security module="x509token" code="second-security" name="Second security profile">
<Param key="certificate" valueType="string">path_to_second_certificate</Params>
<Param key="private_key" valueType="string">path_to_second_pkey_file</Param>
<Param key="private_key_pass" valueType="string">second-password</Param>
</Security>
</SecurityProfile>
Applications¶
Здесь описываются веб-сервисы в элементах Application
:
<Applications>
<!-- Реестр веб-сервисов (Соответствия протоколов-сервисов) -->
<Application name="SampleApp" service="Service">
<InProtocol code="soap11"/>
<OutProtocol code="soap11"/>
</Application>
</Application>
Атрибуты Application
:
Атрибут | Описание | Обязательный |
---|---|---|
name |
уникальный идентификатор, который также будет использоваться в урле | да |
service |
код сервиса, ссылка на сервис описанный в элементах Service |
да |
tns |
неймспейс для веб-сервиса | нет |
url |
regex URL, по умолчанию /wsfactory/api/<Application.name>/ | нет |
Внутри элемента Application
в обязательном порядке должны содержаться элементы
InProtocol
и OutProtocol
.
Атрибуты InProtocol
/OutProtocol
Атрибут | Описание | Обязательный |
---|---|---|
code |
код протокола, ссылка на протокол в элементах Protocol |
да |
security |
код сервиса, ссылка на профиль безопасноти в элементах Service |
да |
Так же внутри каждого из этих протоколов можно задать параметры инициализации, которые могут перекрывать параметры поумолчанию описанные в протоколе:
<Applications>
<!-- Реестр веб-сервисов (Соответствия протоколов-сервисов) -->
<Application name="SampleApp" service="Service">
<InProtocol code="soap11">
<Param key="pretty_print" valueType="bool">true</Param>
</InProtocol>
<OutProtocol code="soap11"/>
</Application>
</Application>
Валидация, схема, загрузка и request¶
Схема конфигурации находиться в пакете по пути wsfactory/schema/wsfactory.xsd. В ней описаны все аспекты файла конфигурации и можно использовать её как документацию к файлу конфигурации.
При первом запросе к сервисам, произойдет чтение файла, и выполнится валидация по схеме. Если файл валидный, то запрос продолжит свое выполнение, wsfactory сконструирует на основе файла конфигурации веб-сервис и передаст ему request.
Если вы допустили ошибку и файл не прошел валидацию, то будет возбуждено исключение
wsfactory.config.ImproperlyConfigured
, с текстом ошибки похожим на формат
вывода утилиты xmllint.
Todo
добавить пример ошибки (pull-request приветствуется)