使用python開發基於soap 的web services 服務

作者:season



序言

web services 已經不再流行,但是,由於它的在接口技術中有着非常重要的地位,同時現在最主要的Web 服務設計模型REST其實也屬於web services 技術範疇。所以我們還是有必要學習一下。

其實 Web Serive 是一項不太容易講清楚技術。他的相關概念包括:

  1. SOA Service-Oriented Architecture
  2. Web Services
  3. SOAP (Simple Object Access Protocol)
  4. WSDL (Web Services Description Language)
  5. UDDI (Universal Description Discovery and Integration)

相關概念

web services 這套複雜的技術如上文所述已經算是過時,但瞭解相關概念還是必要的

SOA

Service Oriented Ambiguity 中文一般理解爲,面向服務架構,簡稱 SOA。
SOA 的提出是在企業計算領域,就是要將緊耦合的系統,劃分爲面向業務的,粗粒度,鬆耦合,無狀態的服務。

SOA 的幾個關鍵
特性:

一種粗粒度、鬆耦合服務架構,服務之間通過簡單、精確定義接口進行通訊,不涉及底層編程接口和通訊模型。

對於 SOA 來說,並不需要太過較真 SOA 到是一個怎樣的架構。只要符合它的定義和規範的軟件系統都可以認爲是 SOA 架構。

現在幾乎所有的 SOA 應用場合都是和 Web Service 綁定的,所以不免有時候這兩個概念混用。不可 否認 Web Service 是現在最適合實現 SOA 的技術,SOA 的走紅在很大程度上歸功於 Web Service 標準的成熟和 應用普及。

web services

Web Service 詳細的描述: Web Service 是一個平臺獨立的,低耦合的,自包含的、基於可編程的 web 的應用程序,可使用開放的 XML(標準通用標記語言下的一個子集)標準來描述、發佈、發現、協調和配置這些應用程序,用於開發分 布式的互操作的應用程序。

在 Web Service 中所有的訪問都通過 SOAP 訪問進行,用 WSDL 定義的接口封裝,通過 UDDI 進行目錄查找所以SOAP、WSDL 和 UDDI 構成了 Web Service 的三要素。

以下簡略介紹這三要素。

SOAP

Simple Object Access Protocol,中文爲簡單對象訪問協議,簡稱 SOAP。 SOAP 是基於 XML 在分散或分佈式的環境中交換信息的簡單的協議。允許服務提供者和服務客戶經過防 火牆在 INTERNET 進行通訊交互。

最多的情況還是還是綁定在HTTP 協議上面傳輸。所以,導致大多數人認爲SOAP 就是HTTP + XML, 或者認爲 SOAP 是 HTTP post 請求的一個專用版本,遵循一種特殊的 XML 消息格式。

WSDL

Web Services Description Language,網絡服務描述語言,簡稱 WSDL。它是一門基於 XML 的語言,用 於描述 Web Services 以及如何對它們進行訪問。

UDDI

UDDI Universal Description, Discovery and Integration",可譯爲“通用描述、發現與集成服務”,簡稱 UDDI。 WSDL 用來描述了訪問特定的 Web Service 的一些相關的信息,那麼在互聯網上,或者是在企業的不同 部門之間,如何來發現我們所需要的 Web Service 呢?而 Web Service 提供商又如何將自己開發的 Web Serivce 公佈到因特網上呢?這就需要使用到 UDDI 了。


環境搭建

python 使用簡單,第三方庫豐富,我們搭建好環境,進行一整套web services 程序的開發。

我們使用 python 3.6 這個較新python 版本

創建conda 環境

conda create --name Web_Services python=3.6
conda activate Web_Services 

導出依賴包

pip freeze > requirements.txt
conda list -e > requirements.txt

依賴包列表

# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: win-64
certifi=2019.6.16=py36_0
lxml=4.3.4=pypi_0
pip=19.1.1=py36_0
python=3.6.8=h9f7ef89_7
pytz=2019.1=pypi_0
setuptools=41.0.1=py36_0
spyne=2.12.16=pypi_0
sqlite=3.28.0=he774522_0
suds-py3=1.3.3.0=pypi_0
vc=14.1=h0510ff6_4
vs2015_runtime=14.15.26706=h3a45250_4
wheel=0.33.4=py36_0
wincertstore=0.2=py36h7fe50ca_0

使用conda 或者pip 批量安裝

pip install -r requirements.txt
conda install --yes --file requirements.txt

在這裏插入圖片描述
但是注意,async=False ,這個參數問題在3.7版本中有問題,spyne 庫會有報錯。因爲在Python3.7裏async變成了關鍵字,關鍵字是不能做變量名的,只要把這個名字改成任意不是關鍵字的詞就好了。


服務端開發

針對Python的WebService開發,最早開發者使用最多的庫是soaplib(官方地址:http://soaplib.github.io/soaplib/2_0/index.html ),
但從其官網可知,其最新版本“soaplib-2.0.0-beta2”從2011年3月發佈後就不再進行更新了。

通過閱讀soaplib的官方文檔,可知其不再維護後已經轉向了一個新的項目:rpclib(官方地址:http://github.com/arskom/rpclib )進行後續開發,但在rpclib的readme中,介紹了rpclib已經更名爲spyne,並將持續進行更新。

綜上,所以選用spyne進行開發了。

服務端樣例代碼:

https://github.com/arskom/spyne/blob/master/examples/helloworld_soap.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#-------------------------------------------------------------------------------
'''
@Author  :   {SEASON}
@License :   (C) Copyright 2013-2022, {OLD_IT_WANG}
@Contact :   {}
@Software:   PyCharm
@File    :   Web_Services_Test20190708 -- Web_Services
@Time    :   2019/7/11 9:03
@Desc    :

'''
#-------------------------------------------------------------------------------
# !/usr/bin/env python
# -*- coding: utf-8 -*-

"""
preference:
    http://spyne.io/docs/2.10/index.html
    https://github.com/arskom/spyne/blob/master/examples/helloworld_soap.py

This is a simple HelloWorld example to show the basics of writing
a webservice using spyne, starting a server, and creating a service
client.
Here's how to call it using suds:

#>>> from suds.client import Client
#>>> hello_client = Client('http://localhost:8000/?wsdl')
#>>> hello_client.service.say_hello('punk', 5)
(stringArray){
   string[] =
      "Hello, punk",
      "Hello, punk",
      "Hello, punk",
      "Hello, punk",
      "Hello, punk",
 }
#>>>

"""
# Application is the glue between one or more service definitions, interface and protocol choices.
from spyne import Application
# @rpc decorator exposes methods as remote procedure calls
# and declares the data types it accepts and returns
from spyne import rpc
# spyne.service.ServiceBase is the base class for all service definitions.
from spyne import ServiceBase
# The names of the needed types for implementing this service should be self-explanatory.
from spyne import Iterable, Integer, Unicode

from spyne.protocol.soap import Soap11
# Our server is going to use HTTP as transport, It’s going to wrap the Application instance.
from spyne.server.wsgi import WsgiApplication


# step1: Defining a Spyne Service
class HelloWorldService(ServiceBase):
    @rpc(Unicode, Integer, _returns=Iterable(Unicode))
    def say_hello(self, name, times):
        """Docstrings for service methods appear as documentation in the wsdl.
        <b>What fun!</b>
        @param name: the name to say hello to
        @param times: the number of times to say hello
        @return  When returning an iterable, you can use any type of python iterable. Here, we chose to use generators.
        """

        for i in range(times):
            yield u'Hello, %s' % name


# step2: Glue the service definition, input and output protocols
soap_app = Application([HelloWorldService], 'spyne.examples.hello.soap',
                       in_protocol=Soap11(validator='lxml'),
                       out_protocol=Soap11())

# step3: Wrap the Spyne application with its wsgi wrapper
wsgi_app = WsgiApplication(soap_app)

if __name__ == '__main__':
    import logging

    from wsgiref.simple_server import make_server

    # configure the python logger to show debugging output
    logging.basicConfig(level=logging.DEBUG)
    logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)

    logging.info("listening to http://127.0.0.1:8000")
    logging.info("wsdl is at: http://localhost:8000/?wsdl")

    # step4:Deploying the service using Soap via Wsgi
    # register the WSGI application as the handler to the wsgi server, and run the http server
    server = make_server('127.0.0.1', 8000, wsgi_app)
    server.serve_forever()

客戶端開發

這方面有兩個python 組件可以使用,分別是:

  • suds-jurko
  • suds-py3

suds-jurko

pip install suds-jurko

文檔
https://bitbucket.org/jurko/suds/wiki/Original%20Documentation

suds-py3

https://github.com/cackharot/suds-py3

pip3 install suds-py3

客戶端樣例代碼

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#-------------------------------------------------------------------------------
'''
@Author  :   {SEASON}
@License :   (C) Copyright 2013-2022, {OLD_IT_WANG}
@Contact :   {[email protected]}
@Software:   PyCharm
@File    :   Web_Services_Test20190708 -- test_client_suds-py3_eg
@Time    :   2019/7/11 23:45
@Desc    :

'''
#-------------------------------------------------------------------------------


import sys
import os
suds_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(suds_path)

from suds.client import Client

def set_log():
    import logging
    logging.basicConfig(level=logging.INFO)
    logging.getLogger('suds.client').setLevel(logging.DEBUG)
    logging.getLogger('suds.transport').setLevel(logging.DEBUG)
    # logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)

def call_service(url):
    client = Client(url, username='bob', password='catbob')
    do_call_service(client, url)

def do_call_service(client, url):
    print("Calling: sayHello()")
    result = client.service.sayHello('Username')

    print("Result: %s" % result)
    a = 10.98
    b = 98.83
    print("Calling: add()")
    sum = client.service.add(a, b)
    print("Result: Sum of %f + %f = %f" % (a,b,sum))

    print("Calling: addDate()")
    from datetime import datetime
    import time
    inputDate = datetime.now()
    dt = client.service.addDate(inputDate, 1)
    print("Result: %s" % dt)

def test(url):
    client = Client(url)
    for p in client.sd[0].ports:
        for m, args in p[1]:
            if len(args) == 0:
                print(client.service[0][m]())

if __name__ == '__main__':
    # set_log()
    url = 'http://localhost:8000/?wsdl'
    if len(sys.argv) > 1:
        url = sys.argv[1]

    call_service(url)
    # test('http://dati.meteotrentino.it/service.asmx?WSDL')
    client1 = Client("http://127.0.0.1:8181/soap/infoservice?wsdl", username='bob', password='catbob')
    print(client1.service.getInfo("Bob"))

    # client2 = Client("http://127.0.0.1:8181/soap/infoservice?wsdl", username='bob', password='catbob')
    # print(client2.service.getInfo("Test2"))
    # 
    # client3 = Client("http://127.0.0.1:8181/soap/infoservice?wsdl", username='bob', password='catbob')
    # print(client3.service.getInfo("Test3"))

參考文獻

1.《web 接口開發與自動化測試 基於Python語言》
2. https://www.cnblogs.com/guanfuchang/p/5985070.html

發佈了35 篇原創文章 · 獲贊 7 · 訪問量 9485
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章