Знакомство с библиотекой Spyne
В данной статье я хочу рассказать о замечательной Python-библиотеке Spyne. Мое знакомство с Spyne началось в тот момент, когда передо мной поставили задачу написать Веб-сервис, который будет принимать и отдавать запросы через SOAP-протокол. Немного погуглив я наткнулся на Spyne, которая является форком библиотеки soaplib. А еще я был удивлен, насколько мало русскоязычной информации встречается о данной библиотеке.
С помощью Spyne можно писать веб-сервисы, которые умеют работать с SOAP, JSON, YAML, а написанный скрипт можно запустить через mod_wsgi Apache. Итак, давайте рассмотрим несколько примеров, напишем работающие скрипты и настроим так, чтобы скрипты работали через apache.
1. SOAP-сервис
Давайте напишем веб-сервис, который будет служить нам переводчиком на английский язык. Наш веб-сервис будет получать запросы, обращаться в Yandex-translator, получать перевод и данный перевод отдавать клиенту. Принимаются входящие запросы в XML-формате. Ответ также будет уходить в XML-формате.
Первым делом необходимо получить API-ключ, чтобы сказать Яндексу, что мы свои. Как можно это сделать, смотрим тут.
Теперь переходим непосредственно к разработке.
Устанавливаем необходимые библиотеки: «pytz», «spyne», а также «yandex_translate». Библиотеки ставятся очень легко через pip.
Код приложения выглядит следующим образом:
from spyne import Application, rpc, ServiceBase, Unicode
from lxml import etree
from spyne.protocol.soap import Soap11
from spyne.protocol.json import JsonDocument
from spyne.server.wsgi import WsgiApplication
from yandex_translate import YandexTranslate
class Soap(ServiceBase):
@rpc(Unicode, _returns=Unicode)
def Insoap(ctx, words):
print(etree.tostring(ctx.in_document))
translate = YandexTranslate('trnsl.1.1.201somesymbols')
tr = translate.translate(words, 'en')
tr_answer = tr['text'][0]
return tr_answer
app = Application([Soap], tns='Translator',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11()
application = WsgiApplication(app)
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('0.0.0.0', 8000, application)
server.serve_forever()
Разберем код:
После импортирования необходимых библиотек, мы создали класс «Soap» с аргументом «ServiceBase». Декоратор "@rpc(Unicode, _returns=Unicode)" определяет тип входящих аргументов («Unicode») и исходящих ответов ("_returns=Unicode"). Список доступных типов аргументов можно посмотреть в официальной документации.. Далее создается метод «Insoap» с аргументами «ctx» и «words». Аргумент «ctx» очень важен, так как в нем содержится много информации о входящих запросах. Строка «print(etree.tostring(ctx.in_document))» выводит на экран входящий xml-запрос, в таком виде, в каком нам его отправил пользователь. В некоторых моментах это может быть важно.
Например, мне в ходе написания веб-сервиса нужно было вытащить входящий xml-запрос и записать в базу данных. Но как вытащить этот xml-запрос не упомянуто в официальной документации Spyne. Burak Arslan (автор Spyne) порекомендовал смотреть в сторону библиотеки lxml. Только после этого я нашел ответ и результат видите в данноме скрипте. Далее наш метод обращается в Яндекс-переводчик и возвращает клиенту полученный от Яндекс-переводчика результат.
Переменная «app» определяет настройки нашего веб-сервиса: «Application([Soap]» — указывается, какой класс инициализируется (их может быть несколько), параметры «in_protocol» и «out_protocol» определяет тип входящих и исходящих запросов, в нашем случае это SOAP v1.1.
Строкой «application = WsgiApplication(app)» определяется, чтобы наш скрипт мог работать через wsgi.
Важно! имя переменного обязательно должен быть «application», чтобы наше приложение мог работать через apache с помощью mod_wsgi. Последующие строки кода инициализирует и запускает Веб-сервер по порту 8000.
Запускаем скрипт и можно приступать к тестированию. Для этих целей я использую SoapUI. Удобство состоит в том, что после запуска и настройки для работы с SOAP сервером, SoapUI автоматически формирует xml-запрос. Настроимся на URL: localhost:8000?wsdl (при условии, что скрипт запущен на локальной машине), и наш xml-запрос выглядит следующим образом:
Тело xml-запроса
<soapenv:Envelope xmlns:soapenv=«schemas.xmlsoap.org/soap/envelope» xmlns:tran=«Translator»>
<soapenv:Header/>
<soapenv:Body>
<tran:Insoap>
<tran:words>Тестируем наше приложение</tran:words>
</tran:Insoap>
</soapenv:Body>
</soapenv:Envelope>
<soapenv:Header/>
<soapenv:Body>
<tran:Insoap>
<tran:words>Тестируем наше приложение</tran:words>
</tran:Insoap>
</soapenv:Body>
</soapenv:Envelope>
Наш веб-сервис дал следующий ответ:
Ответ от сервера
<soap11env:Envelope xmlns:soap11env=«schemas.xmlsoap.org/soap/envelope» xmlns:tns=«Translator»>
<soap11env:Body>
<tns:InsoapResponse>
<tns:InsoapResult>Test our app</tns:InsoapResult>
</tns:InsoapResponse>
</soap11env:Body>
</soap11env:Envelope>
<soap11env:Body>
<tns:InsoapResponse>
<tns:InsoapResult>Test our app</tns:InsoapResult>
</tns:InsoapResponse>
</soap11env:Body>
</soap11env:Envelope>
Все просто, не правда ли?
2. REST-сервис
Предположим, что теперь у нас поменялось тех. задание, и нужно сделать веб-сервис, который работает через JSON. Что делать? Переписывать наш сервис на другом фреймворке, например Django Rest Framework или Flask? или можно обойтись меньшими усилиями? Да, можно! И нужно!
Библиотека Spyne нам в помошь.
Все что потребуется поменять в нашем приложении, это переменную «app» привести к следующему виду:
app = Application([Soap], tns='Translator',
in_protocol=JsonDocument(validator='soft'),
out_protocol=JsonDocument())
Запускаем наш веб-сервис и тестируемся.
Наш JSON-запрос выглядит так:
Тело JSON-запроса
{«Insoap»: {«words»:«тестируем наш веб-сервис. Используем JSON»}}
Веб сервер вернул следующий ответ:
Ответ веб-сервера
«test our web service. Use JSON»
3. Вывод в продакшн
Для запуска нашего веб-сервиса через apache, необходимо на сервер установить и настроить веб-сервер apache и mod_wsgi. Данные работы несложно выполнить, опираясь на документацию. Кроме этого, в нашем скрипте мы должны удалить следующие строки:
Строки для удаления
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('0.0.0.0', 8000, application)
server.serve_forever()
Ура! Наш веб-сервис готов к использованию в эксплуатации.
P.S. о дополнительных возможностях Spyne (а их немало) всегда можно ознакомиться на официальном сайте, чего я вам очень рекомендую.
Поделиться с друзьями
Комментарии (5)
Prostakov_Alexey
30.07.2017 20:48Если Вы упомянули про SoapUI хорошо бы написать, какой адрес WSDL описания для создания проекта. http://localhost:8000/Translator?wsdl А так очень полезная статья, я делаю по старинке через flask, предложенный способ намного лучше и проще.
alexZzZzZzZ
Мне кажется, или ответ сервера в режиме REST-сервиса не похож на json?
alexZzZzZzZ
А вот возможность быстро поменять тип транспорта — это прекрасно.
Где еще можно запрос с http json поменять на zeromq очередь в одну строку?
Вопрос только в производительности.
maxpy
Если вы подразумеваете, что ответ не в формате «key»: «value», то да, ответ отличается. Но все REST-клиенты понимают ответ как json, будь это SoapUI, или замечательный requests.