WebService開發(fā): 服務(wù)端[Python] + 客戶端[python & nodejs]

WebService是什么

WebService是一種跨編程語(yǔ)言和跨操作系統(tǒng)平臺(tái)的遠(yuǎn)程調(diào)用技術(shù)。

跨編程語(yǔ)言:就是說(shuō)服務(wù)端程序采用python編寫辜伟,客戶端程序則可以采用其他編程語(yǔ)言編寫(nodejs、java等)筝野;
跨操作系統(tǒng)平臺(tái):就是說(shuō)服務(wù)端程序和客戶端程序可以在不同的操作系統(tǒng)上運(yùn)行嫌松;
遠(yuǎn)程調(diào)用:就是一臺(tái)計(jì)算機(jī)A上的程序可以調(diào)用另一臺(tái)計(jì)算機(jī)B上一個(gè)對(duì)象的方法。eg: 銀聯(lián)提供給商場(chǎng)的pos刷卡系統(tǒng)邑蒋、天氣預(yù)報(bào)系統(tǒng)等姓蜂;

SOAP協(xié)議是什么

WebService通過(guò)http協(xié)議發(fā)送請(qǐng)求和接收結(jié)果時(shí),發(fā)送的請(qǐng)求內(nèi)容和結(jié)果內(nèi)容都是采用XML格式封裝的医吊,并增加一些特定的HTTP消息頭以聲明HTTP消息的內(nèi)容格式钱慢。這些特定的HTTP消息頭和XML內(nèi)容格式就是SOAP協(xié)議。
簡(jiǎn)而言之卿堂,SOAP協(xié)議 = HTTP協(xié)議 + XML數(shù)據(jù)格式

WSDL是什么

好比我們?nèi)ド痰曩I東西束莫,首先要知道商店里有什么東西可買,然后再來(lái)購(gòu)買草描,商家的做法就是張貼廣告海報(bào)麦箍。 WebService也一樣,WebService客戶端要調(diào)用一個(gè)WebService服務(wù)陶珠,首先要有知道這個(gè)服務(wù)的地址在哪挟裂,以及這個(gè)服務(wù)里有什么方 法可以調(diào)用,所以揍诽,WebService務(wù)器端首先要通過(guò)一個(gè)WSDL文件來(lái)說(shuō)明自己家里有啥服務(wù)可以對(duì)外調(diào)用诀蓉,服務(wù)是什么(服務(wù)中有哪些方法,方法接受 的參數(shù)是什么暑脆,返回值是什么)渠啤,服務(wù)的網(wǎng)絡(luò)地址用哪個(gè)url地址表示,服務(wù)通過(guò)什么方式來(lái)調(diào)用添吗。

WSDL(Web Services Description Language)就是這樣一個(gè)基于XML的語(yǔ)言沥曹,用于描述Web Service及其函數(shù)、參數(shù)和返回值。它是WebService客戶端和服務(wù)器端都 能理解的標(biāo)準(zhǔn)格式妓美。因?yàn)槭腔赬ML的僵腺,所以WSDL既是機(jī)器可閱讀的,又是人可閱讀的壶栋,這將是一個(gè)很大的好處辰如。一些最新的開發(fā)工具既能根據(jù)你的 Web service生成WSDL文檔,又能導(dǎo)入WSDL文檔贵试,生成調(diào)用相應(yīng)WebService的代理類代碼琉兜。

WSDL 文件保存在Web服務(wù)器上,通過(guò)一個(gè)url地址就可以訪問(wèn)到它毙玻⊥泱客戶端要調(diào)用一個(gè)WebService服務(wù)之前,要知道該服務(wù)的WSDL文件的地址桑滩。 WebService服務(wù)提供商可以通過(guò)兩種方式來(lái)暴露它的WSDL文件地址:1.注冊(cè)到UDDI服務(wù)器梧疲,以便被人查找;2.直接告訴給客戶端調(diào)用者施符。

WebService開發(fā)

1.服務(wù)端開發(fā)(基于python—spyne庫(kù))

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
    server.py
    ~~~~~~~~~~~~~~~~~~~~~~~

    Description of this file


    :author: nut
    :copyright: (c) 2020, Comcat
    :date created: 2020-12-02
    :python version: 3.5
"""
# Application is the glue between one or more service definitions, interface and protocol choices.
from spyne.application import Application
# @rpc 修飾器將方法公開為遠(yuǎn)程過(guò)程調(diào)用,并聲明其接收和返回的數(shù)據(jù)類型
from spyne.decorator import rpc
# spyne.service.ServiceBase是所有服務(wù)定義的基類
from spyne import ServiceBase
# 數(shù)據(jù)類型
from spyne import Integer, Unicode, Array, ComplexModel, Iterable, String
# soap1.1標(biāo)準(zhǔn)
from spyne.protocol.soap import Soap11
# 我們的服務(wù)是通過(guò)http進(jìn)行傳輸?shù)睦拚遥琖sgiApplication將包裝Application實(shí)例
from spyne.server.wsgi import WsgiApplication
# python內(nèi)置的wsgi服務(wù)器模塊:wsgiref戳吝,用于創(chuàng)建wsgi服務(wù)
from wsgiref.simple_server import make_server

# step1: 自定義數(shù)據(jù)結(jié)構(gòu)
class Person(ComplexModel):
    name = Unicode
    age = Integer

class PeopleResponse(ComplexModel):
    name = Person
    message = Unicode

# step2: 定義服務(wù)
class HelloWorldService(ServiceBase):
    @rpc(Unicode, Integer, _returns=Iterable(Unicode))
    def say_hello(self, name, times):
        for i in range(times):
            yield "Hello %s, It's the %s time to meet you." % (name, i + 1)

    @rpc(Array(Person), _returns=Iterable(Unicode))
    def say_hello_1(self, persons):
        print('-------say_hello_1()--------')
        if not persons:
            yield 'None'
        for person in persons:
            print('name is : %s, age is %s.' % (person.name, person.age))
            yield 'name is : %s, age is %s.' % (person.name, person.age)


class HelloWorldService2(ServiceBase):
    @rpc(Array(String), _returns=Iterable(Unicode))
    def say_hello_2(self, persons):
        if not persons:
            yield 'None'
        for person in persons:
            yield person

    @rpc(Person, _returns=PeopleResponse)
    def say_hello_3(self, person):
        if not person:
            return {}
        else:
            # return PeopleResponse(name=People(**person))
            return {
                "name": person,
                "message": 'name is : %s, age is %s.' % (person.name, person.age)
            }

# step3: 
application = Application([HelloWorldService, HelloWorldService2],
                          'spyne.examples.hello',
                          in_protocol=Soap11(validator='lxml'),
                          out_protocol=Soap11())
# step4:
wsgi_application = WsgiApplication(application)

if __name__ == '__main__':
    import logging

    host = '127.0.0.1'
    port = 8902

    logging.basicConfig(level=logging.DEBUG)
    # 指定日志記錄器的名稱,設(shè)置日志記錄級(jí)別
    logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
    logging.info("listening to http://127.0.0.1:8902")
    logging.info("wsdl is at: http://localhost:8902/?wsdl")
  
    # step5:  創(chuàng)建wsgi服務(wù)
    server = make_server(host, port, wsgi_application)
    server.serve_forever()

2. 客戶端開發(fā)(Python & Nodejs)

suds - Python client

需安裝python第三方庫(kù)suds:pip install suds-py3

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
    client_requests_suds.py
    ~~~~~~~~~~~~~~~~~~~~~~~

    模擬客戶端請(qǐng)求——suds版本

    :author: nut
    :copyright: (c) 2020, Comcat
    :date created: 2020-12-01
    :python version: 3.7
"""
from suds.client import Client

host = '127.0.0.1'
port = 8902

client = Client('http://%s:%s/?wsdl' % (host, port))
# print(client) # 打印wsdl內(nèi)容
# print('=' * 20)

persons = client.service.say_hello('zhangsan', 2)
print(persons)

print('-' * 20)
person = {}
person['name'] = 'zhangsan'
person['age'] = 23

persons = client.factory.create('PersonArray')
persons.Person.append(person)
persons.Person.append(person)
person = client.service.say_hello_1(persons)
print(person)

print('=' * 20)
persons = client.factory.create('stringArray')
persons.string.append('lisi')
persons.string.append('zhangsan')
person = client.service.say_hello_2(persons)
print(person)

print('=' * 20)
pers = {"name": u"張三", "age": 23}
result = client.service.say_hello_3(pers)
print(result)

zeep - Python client

需安裝python第三方庫(kù)zeep:pip install zeep

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
    client_requests_zeep.py
    ~~~~~~~~~~~~~~~~~~~~~~~

    模擬客戶端請(qǐng)求——zeep版本

    :author: nut
    :copyright: (c) 2020, Comcat
    :date created: 2020-12-02
    :python version: 3.5
"""
from zeep import Client

ip = '127.0.0.1'
port = 8901
client = Client("http://%s:%s/?wsdl" % (ip, port))
# print(client.wsdl.dump()) # 解析wsdl
# print('=' * 20)

### say_hello
r = client.service.say_hello('zhansgan', 3)
print(r)
print('-' * 20)

### say_hello_1
factory = client.type_factory("ns0")
person = factory.Person(name='zhangsan', age=23)
persons = factory.PersonArray([person, person])
r = client.service.say_hello_1(persons)
print(r)
print('-' * 20)

### say_hello_2
factory = client.type_factory("ns0")
persons = factory.stringArray(["zhansgan", "lisi"])
r = client.service.say_hello_2(persons)
print(r)
print('-' * 20)

### say_hello_3
# factory = client.type_factory("ns0")
person = {"name": u"張三", "age": 23}
r = client.service.say_hello_3(person)
print(r)

<strong-soap> - node client (親測(cè)可用)

需安裝node第三方庫(kù)strong-soap:npm install strong-soap

var soap = require("strong-soap").soap;

var ip = '127.0.0.1';
var port = 8901;

var WSDL_URL = "http://" + ip + ":" + port + "/?wsdl";

soap.createClient(WSDL_URL, {}, function (err, client) {
    client.setEndpoint(WSDL_URL)
//    console.log(client)
//    console.log("===============================================")
//    console.log(client.describe())
//    console.log("===============================================")

    // 調(diào)用say_hello方法 (親測(cè)可行)
    client.say_hello({"name": "ccc", "times": 2}, function (err, result) {
        console.log("<say_hello>Err: ")
        console.log(err)
        console.log("<say_hello>Result: ")
        console.log(result)
        console.log("===============================================")
    })

    // say_hello_1 參數(shù)結(jié)構(gòu)
    let arg_1 =  {
        "persons": {
            "Person": [{"name": "zzz", "age": 23}, {"name": "aaa", "age": 18}
            ]
        }
    }
    // 調(diào)用say_hello_1方法 (傳參可行)
    client.say_hello_1(arg_1, function (err, result) {
        console.log("<say_hello_1>Err: ")
        console.log(err)
        console.log("<say_hello_1>Result: ")
        console.log(result)
        console.log("===============================================")
    })

    // say_hello_2 參數(shù)結(jié)構(gòu)
    let arg_2 = {
        persons: {
            string: ["aaa", "zzz"]
        }
    }
    // 調(diào)用say_hello_2方法 (傳參可行)
    client.say_hello_2(arg_2, function (err, result) {
        console.log("<say_hello_2>Err: ")
        console.log(err)
        console.log("<say_hello_2>Result: ")
        console.log(result)
        console.log("===============================================")
    })

    // say_hello_3 參數(shù)結(jié)構(gòu)
    let arg_3 = {
        person: {
            name: "aaa",
            age: 24
        }
    }
    // 調(diào)用say_hello_3方法 (傳參可行)
    client.say_hello_3(arg_3, function (err, result) {
        console.log("<say_hello_3>Err: ")
        console.log(err)
        console.log("<say_hello_3>Result: ")
        console.log(result)
        console.log("===============================================")
    })
})

node:strong-soap開發(fā)客戶端贯涎,難點(diǎn)在于: complexType參數(shù)的構(gòu)造听哭。
上述node客戶端腳本執(zhí)行結(jié)果如下:

# 結(jié)果:
<say_hello>Err:
null
<say_hello>Result:
{ say_helloResult:
   { string:
      [ 'Hello ccc, It\'s the 1 time to meet you.',
        'Hello ccc, It\'s the 2 time to meet you.' ] } }
===============================================
<say_hello_1>Err:
null
<say_hello_1>Result:
{ say_hello_1Result:
   { string: [ 'name is : zzz, age is 23.', 'name is : aaa, age is 18.' ] } }
===============================================
<say_hello_2>Err:
null
<say_hello_2>Result:
{ say_hello_2Result: { string: [ 'aaa', 'zzz' ] } }
===============================================
<say_hello_3>Err:
null
<say_hello_3>Result:
{ say_hello_3Result:
   { name: { name: 'aaa', age: 24 },
     message: 'name is : aaa, age is 24.' } }
===============================================

源碼:https://gitee.com/handsomeCzp/web-service-sample

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市塘雳,隨后出現(xiàn)的幾起案子陆盘,更是在濱河造成了極大的恐慌,老刑警劉巖败明,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隘马,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡妻顶,警方通過(guò)查閱死者的電腦和手機(jī)酸员,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)讳嘱,“玉大人幔嗦,你說(shuō)我怎么就攤上這事×ぬ叮” “怎么了邀泉?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我汇恤,道長(zhǎng)庞钢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任屁置,我火速辦了婚禮焊夸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蓝角。我一直安慰自己阱穗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布使鹅。 她就那樣靜靜地躺著揪阶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪患朱。 梳的紋絲不亂的頭發(fā)上鲁僚,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音裁厅,去河邊找鬼冰沙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛执虹,可吹牛的內(nèi)容都是我干的拓挥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼袋励,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼侥啤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起茬故,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盖灸,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后磺芭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赁炎,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年钾腺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甘邀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡垮庐,死狀恐怖松邪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哨查,我是刑警寧澤逗抑,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響邮府,放射性物質(zhì)發(fā)生泄漏荧关。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一褂傀、第九天 我趴在偏房一處隱蔽的房頂上張望忍啤。 院中可真熱鬧,春花似錦仙辟、人聲如沸同波。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)未檩。三九已至,卻和暖如春粟焊,著一層夾襖步出監(jiān)牢的瞬間冤狡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工项棠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悲雳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓香追,卻偏偏與公主長(zhǎng)得像合瓢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子翅阵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354