soap协议_Go和SOAP
在REST和RPC大行其道的今天,支持SOAP(簡(jiǎn)答對(duì)象訪問(wèn)協(xié)議)作為Web服務(wù)消息交換協(xié)議的情況是越來(lái)越少了。但在一些遺留系統(tǒng)中,尤其是采用微軟技術(shù)棧的服務(wù)系統(tǒng)中,SOAP依然占有一席之地,比如在一些醫(yī)院院內(nèi)的IT系統(tǒng)中。
Go語(yǔ)言誕生后,主流的Web Service設(shè)計(jì)已經(jīng)開(kāi)始過(guò)渡到REST和RPC,Go相關(guān)開(kāi)源項(xiàng)目也以對(duì)REST和RPC的支持為主。而對(duì)SOAP的支持則少而零散,社區(qū)里也沒(méi)有對(duì)SOAP支持的重量級(jí)開(kāi)源項(xiàng)目,在awesome go的各種list中也難覓有關(guān)SOAP的推薦項(xiàng)目的身影。
但Gopher世界還是有以client身份與SOAP service交互或是實(shí)現(xiàn)SOAP server的需求的。在這篇文章中,我就和大家一起來(lái)探索一下如何基于一些開(kāi)源項(xiàng)目,使用Go實(shí)現(xiàn)SOAP client和SOAP Server的。
一.SOAP簡(jiǎn)介如果你覺(jué)得SOAP這個(gè)協(xié)議很陌生也不奇怪,因?yàn)镾OAP協(xié)議誕生于“遙遠(yuǎn)”的1998年,2000年才提交到標(biāo)準(zhǔn)化組織。SOAP是一種消息傳遞協(xié)議規(guī)范,用于在計(jì)算機(jī)網(wǎng)絡(luò)的Web服務(wù)中實(shí)現(xiàn)交換結(jié)構(gòu)化信息。其目的是促進(jìn)可擴(kuò)展性、中立性和獨(dú)立性。它使用XML作為承載消息的格式,并依賴(lài)于應(yīng)用層協(xié)議,通常是HTTP或SMTP(簡(jiǎn)單郵件傳輸協(xié)議),用于消息協(xié)商和傳輸。經(jīng)過(guò)若干年的演進(jìn),其主要binding的協(xié)議是http,其支持SMTP Binding已經(jīng)極少有應(yīng)用了。現(xiàn)在,我們可以不嚴(yán)謹(jǐn)?shù)恼f(shuō),SOAP可以理解為“xml over http”。并且從SOAP Body的形式來(lái)看,SOAP也像是一種使用XML作為序列化編碼格式的RPC調(diào)用。
SOAP目前存在兩個(gè)版本:1.1和1.2版本。一些比較old的SOAP服務(wù)僅支持1.1版本,而一些新的SOAP服務(wù)則兩個(gè)版本都支持。
下面是SOAP協(xié)議的通用結(jié)構(gòu):
基于這個(gè)結(jié)構(gòu),我們看看SOAP(over http)的Request和Response的樣子:
關(guān)于SOAP協(xié)議的更多細(xì)節(jié),可以參見(jiàn)SOAP協(xié)議規(guī)范,這里限于篇幅就不細(xì)說(shuō)了。
二.環(huán)境準(zhǔn)備本文中使用的Go語(yǔ)言版本為go 1.11.2。
獲取wsdl文件
現(xiàn)在在互聯(lián)網(wǎng)上要找到一個(gè)面向公共的、免費(fèi)的SOAP服務(wù)著實(shí)困難。free-web-services.com上的很多服務(wù)已經(jīng)不提供SOAP服務(wù)了,并且多數(shù)提供SOAP的服務(wù)也已經(jīng)打不開(kāi)頁(yè)面了。在本文中,我們將使用www.dneonline.com/calculator.asmx這個(gè)calculator服務(wù),至少目前它還是ready的(不過(guò)也不保證它在將來(lái)能一直ready)。
我們可以通過(guò)下面命令獲得這個(gè)calculator服務(wù)的WSDL文件。
$cd /Users/tony/go/src/github.com/bigwhite/experiments/go-soap/pkg$curl http://www.dneonline.com/calculator.asmx\?WSDL > calculator.wsdl
$cat calculator.wsdl
<?xml version="1.0" encoding="utf-8"?>
l:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
... ...l:service name="Calculator">l:port name="CalculatorSoap" binding="tns:CalculatorSoap">"http://www.dneonline.com/calculator.asmx" />l:port>l:port name="CalculatorSoap12" binding="tns:CalculatorSoap12">"http://www.dneonline.com/calculator.asmx" />l:port>l:service>l:definitions>
這個(gè)calculator.wsdl是后續(xù)實(shí)現(xiàn)soap client和soap server的基礎(chǔ)。
2. 根據(jù)wsdl文件生成SOAP package
雖然Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中擁有比較完善的XML操作package,但是我們也沒(méi)有必要從頭開(kāi)始進(jìn)行SOAP協(xié)議的封裝和解包。github上面的gowsdl項(xiàng)目可以幫助我們基于calculator.wsdl自動(dòng)生成實(shí)現(xiàn)SOAP Client和SOAP Server所要使用的各種方法和結(jié)構(gòu)體,這也是我們后續(xù)實(shí)現(xiàn)SOAP Client和SOAP Server的基本原理。
$go get github.com/hooklift/gowsdl/...$gowsdl -i calculator.wsdl
Reading file /Users/tony/go/src/github.com/bigwhite/experiments/go-soap/pkg/calculator.wsdl
Done
$tree
.
├── calculator.wsdl
└── myservice
? ?└── myservice.go
1 directory, 2 files
gowsdl根據(jù)calculator.wsdl生成了myservice.go,所有有關(guān)calculator soap service的結(jié)構(gòu)體和方法都在這個(gè)Go源文件中。有了這個(gè)package,我們就可以來(lái)實(shí)現(xiàn)soap客戶(hù)端了。
三.實(shí)現(xiàn)SOAP客戶(hù)端我們實(shí)現(xiàn)一個(gè)SOAP客戶(hù)端,用于調(diào)用www.dneonline.com/calculator服務(wù)中提供的Add方法來(lái)進(jìn)行加法計(jì)算。
我們先在$GOPATH/src/github.com/bigwhite/experiments/go-soap下面建立client目錄,進(jìn)入client目錄,創(chuàng)建client的main.go文件。
在前面根據(jù)calculator.wsdl生成的myservice.go文件中,我們找到了NewCalculatorSoap方法,該方法會(huì)返回一個(gè)到對(duì)應(yīng)服務(wù)的client實(shí)例,通過(guò)該soap client實(shí)例,我們可以調(diào)用其包含的Add方法,我們來(lái)看一下main.go中的代碼實(shí)現(xiàn):
package mainimport (
? ?"fmt"
? ?soap "github.com/bigwhite/experiments/go-soap/pkg/myservice"
)
func main() {
? ?c := soap.NewCalculatorSoap("", false, nil)
? ?r, err := c.Add(&soap.Add{
? ? ? ?IntA: 2,
? ? ? ?IntB: 3,
? ?})
? ?if err != nil {
? ? ? ?fmt.Println(err)
? ? ? ?return
? ?}
? ?fmt.Println(r.AddResult)
}
Add方法的參數(shù)為soap.Add結(jié)構(gòu)體的實(shí)例,Add結(jié)構(gòu)有兩個(gè)字段IntA和IntB,分別代表了兩個(gè)加數(shù)。我們來(lái)執(zhí)行一下該client實(shí)現(xiàn):
$go run main.go2019/01/08 12:54:31 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Add xmlns="http://tempuri.org/"><intA>2intA><intB>3intB>Add>Body>Envelope>
2019/01/08 12:54:31 xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><AddResponse xmlns="http://tempuri.org/"><AddResult>5AddResult>AddResponse>soap:Body>soap:Envelope>
5
我們看到client輸出了加法服務(wù)調(diào)用后的正確結(jié)果:5。
四.實(shí)現(xiàn)SOAP服務(wù)下面我們?cè)賮?lái)實(shí)現(xiàn)一個(gè)類(lèi)似www.dneonline.com/calculator的服務(wù),由于只是demo,我們只實(shí)現(xiàn)Add方法,其他方法的“套路”是一樣的。
我們?cè)?GOPATH/src/github.com/bigwhite/experiments/go-soap下面建立server目錄,進(jìn)入server目錄,創(chuàng)建server的main.go文件。pkg/myservice/myservice.go中只是SOAP層協(xié)議數(shù)據(jù)負(fù)荷的marshal和unmarshal操作,并沒(méi)有網(wǎng)絡(luò)層面的支持,因此我們需要自己建立http server框架,我們就使用Go標(biāo)準(zhǔn)庫(kù)http server。代碼結(jié)構(gòu)如下:
package mainimport (
? ?"encoding/xml"
? ?"fmt"
? ?"io/ioutil"
? ?"log"
? ?"net/http"
? ?"regexp"
? ?soap "github.com/bigwhite/experiments/go-soap/pkg/myservice"
)
func main() {
? ?s := NewSOAPServer("localhost:8080")
? ?log.Fatal(s.ListenAndServe())
}
func NewSOAPMux() *http.ServeMux {
? ?mux := http.NewServeMux()
? ?mux.HandleFunc("/", soapHandler)
? ?return mux
}
func NewSOAPServer(addr string) *http.Server {
? ?mux := NewSOAPMux()
? ?server := &http.Server{
? ? ? ?Handler: mux,
? ? ? ?Addr: ? ?addr,
? ?}
? ?return server
}
func soapHandler(w http.ResponseWriter, r *http.Request) {
? ... ...
}
這個(gè)SOAP server的外層結(jié)構(gòu)與普通http server并無(wú)太多差異。我們重點(diǎn)要關(guān)注的是soapHandler的實(shí)現(xiàn)邏輯。
func soapHandler(w http.ResponseWriter, r *http.Request) {? ?rawBody, err := ioutil.ReadAll(r.Body)
? ?if err != nil {
? ? ? ?w.WriteHeader(http.StatusInternalServerError)
? ? ? ?return
? ?}
? ?// match method
? ?var res interface{}
? ?m := regexp.MustCompile(`)if m.MatchString(string(rawBody)) {
? ? ? ?res = processAdd(rawBody)
? ?} else {
? ? ? ?res = nil
? ? ? ?fmt.Println("the method requested is not available")
? ?}
? ?v := soap.SOAPEnvelope{
? ? ? ?Body: soap.SOAPBody{
? ? ? ? ? ?Content: res,
? ? ? ?},
? ?}
? ?w.Header().Set("Content-Type", "text/xml")
? ?x, err := xml.MarshalIndent(v, "", " ?")if err != nil {
? ? ? ?w.WriteHeader(http.StatusInternalServerError)return
? ?}
? ?w.WriteHeader(http.StatusOK)
? ?w.Write(x)return
}
我們看到:
首先,我們從http body中讀取出原始數(shù)據(jù);
接下來(lái),我們通過(guò)一個(gè)正則表達(dá)式去匹配原始數(shù)據(jù),如果匹配到方法,則進(jìn)入方法的處理函數(shù)processAdd;否則提示方法不存在;
最后將processAdd的返回結(jié)果marshall為SOAP格式后,返回給client端。
processAdd是真正執(zhí)行服務(wù)算法的函數(shù):
func processAdd(body []byte) *soap.AddResponse {? ?envlop := &soap.SOAPEnvelope{
? ? ? ?Body: soap.SOAPBody{
? ? ? ? ? ?Content: &soap.Add{},
? ? ? ?},
? ?}
? ?err := xml.Unmarshal(body, envlop)
? ?if err != nil {
? ? ? ?fmt.Println("xml Unmarshal error:", err)
? ? ? ?return nil
? ?}
? ?fmt.Println(envlop.Body.Content)
? ?r, ok := envlop.Body.Content.(*soap.Add)
? ?if !ok {
? ? ? ?return nil
? ?} else {
? ? ? ?return &soap.AddResponse{
? ? ? ? ? ?AddResult: r.IntA + r.IntB,
? ? ? ?}
? ?}
}
processAdd首先將rawBody unmarshal到一個(gè)SOAPEnvelope結(jié)構(gòu)體中,從而得到SOAP envelope中Body中的方法的輸入?yún)?shù)的值:IntA和IntB。將兩個(gè)加數(shù)的和賦值給AddResult,作為AddResponse的值返回。
我們啟動(dòng)一下該SOAP server,并修改一下前面client所要連接的soap server的地址,并讓client向我們自己實(shí)現(xiàn)的soap server發(fā)起服務(wù)請(qǐng)求調(diào)用:
1.修改client main.go
$GOPATH/src/github.com/bigwhite/experiments/go-soap/client/main.go? ?//c := soap.NewCalculatorSoap("", false, nil)
? ?c := soap.NewCalculatorSoap("http://localhost:8080/", false, nil)
2.啟動(dòng)soap server
$GOPATH/src/github.com/bigwhite/experiments/go-soap/server git:(master) ? $go run main.go3. 啟動(dòng)client
$go run main.go2019/01/08 14:55:20 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Add xmlns="http://tempuri.org/"><intA>2intA><intB>3intB>Add>Body>Envelope>
2019/01/08 14:55:20 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
?<Body xmlns="http://schemas.xmlsoap.org/soap/envelope/">
? ?<AddResponse xmlns="http://tempuri.org/">
? ? ?<AddResult>5AddResult>
? ?AddResponse>
?Body>
Envelope>
5
4. server console輸出
&{{http://tempuri.org/ Add} 2 3}我們看到,我們的client成功調(diào)用了我們實(shí)現(xiàn)的SOAP Server的服務(wù)方法,并獲得了正確的結(jié)果。一個(gè)簡(jiǎn)單的SOAP server就這么實(shí)現(xiàn)了。不過(guò)明眼的朋友肯定已經(jīng)看出代碼中的問(wèn)題了,那就是method match那塊僅僅適用于demo,在真正的服務(wù)中,服務(wù)會(huì)有很多method,我們需要一種規(guī)范的、通用的匹配機(jī)制,一種可以通過(guò)SOAP Body匹配來(lái)做,另一種可以通過(guò)http header中的SOAP Action 來(lái)匹配(僅適用于SOAP 1.1,SOAP 1.2版本去掉了SOAP Action)。這里就留給大家自己去發(fā)揮吧。
五.小結(jié)如果不是碰到了基于SOAP的遺留系統(tǒng),我想我是不會(huì)研究SOAP的,畢竟基于SOAP的系統(tǒng)正在逐漸消逝在大眾的視野中。上面的demo應(yīng)該可以讓大家對(duì)如何用Go與SOAP系統(tǒng)交互有了一個(gè)粗淺的認(rèn)識(shí)。這里算是一個(gè)不錯(cuò)的起點(diǎn)。如果大家對(duì)于Go與SOAP有更深刻地研究或者有更好的有關(guān)SOAP的開(kāi)源項(xiàng)目,歡迎交流。
文中源碼在這里https://github.com/bigwhite/experiments/tree/master/go-soap可以找到。
我的網(wǎng)課“Kubernetes實(shí)戰(zhàn):高可用集群搭建、配置、運(yùn)維與應(yīng)用”在慕課網(wǎng)上線了,感謝小伙伴們學(xué)習(xí)支持!
我要發(fā)短信:企業(yè)級(jí)短信平臺(tái)定制開(kāi)發(fā)專(zhuān)家 https://51smspush.com/
smspush : 可部署在企業(yè)內(nèi)部的定制化短信平臺(tái),三網(wǎng)覆蓋,不懼大并發(fā)接入,可定制擴(kuò)展; 短信內(nèi)容你來(lái)定,不再受約束, 接口豐富,支持長(zhǎng)短信,簽名可選。
著名云主機(jī)服務(wù)廠商DigitalOcean發(fā)布最新的主機(jī)計(jì)劃,入門(mén)級(jí)Droplet配置升級(jí)為:1 core CPU、1G內(nèi)存、25G高速SSD,價(jià)格5$/月。有使用DigitalOcean需求的朋友,可以打開(kāi)這個(gè)鏈接地址:https://m.do.co/c/bff6eed92687 開(kāi)啟你的DO主機(jī)之路。
我的聯(lián)系方式:
微博:https://weibo.com/bigwhite20xx
微信公眾號(hào):iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite
商務(wù)合作方式:撰稿、出書(shū)、培訓(xùn)、在線課程、合伙創(chuàng)業(yè)、咨詢(xún)、廣告合作。
? 2019, bigwhite. 版權(quán)所有.
總結(jié)
以上是生活随笔為你收集整理的soap协议_Go和SOAP的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python 3 5的值_python3
- 下一篇: python绘图教程_pyplot绘图教