soap协议_Go和SOAP
在REST和RPC大行其道的今天,支持SOAP(簡答對象訪問協議)作為Web服務消息交換協議的情況是越來越少了。但在一些遺留系統中,尤其是采用微軟技術棧的服務系統中,SOAP依然占有一席之地,比如在一些醫院院內的IT系統中。
Go語言誕生后,主流的Web Service設計已經開始過渡到REST和RPC,Go相關開源項目也以對REST和RPC的支持為主。而對SOAP的支持則少而零散,社區里也沒有對SOAP支持的重量級開源項目,在awesome go的各種list中也難覓有關SOAP的推薦項目的身影。
但Gopher世界還是有以client身份與SOAP service交互或是實現SOAP server的需求的。在這篇文章中,我就和大家一起來探索一下如何基于一些開源項目,使用Go實現SOAP client和SOAP Server的。
一.SOAP簡介如果你覺得SOAP這個協議很陌生也不奇怪,因為SOAP協議誕生于“遙遠”的1998年,2000年才提交到標準化組織。SOAP是一種消息傳遞協議規范,用于在計算機網絡的Web服務中實現交換結構化信息。其目的是促進可擴展性、中立性和獨立性。它使用XML作為承載消息的格式,并依賴于應用層協議,通常是HTTP或SMTP(簡單郵件傳輸協議),用于消息協商和傳輸。經過若干年的演進,其主要binding的協議是http,其支持SMTP Binding已經極少有應用了。現在,我們可以不嚴謹的說,SOAP可以理解為“xml over http”。并且從SOAP Body的形式來看,SOAP也像是一種使用XML作為序列化編碼格式的RPC調用。
SOAP目前存在兩個版本:1.1和1.2版本。一些比較old的SOAP服務僅支持1.1版本,而一些新的SOAP服務則兩個版本都支持。
下面是SOAP協議的通用結構:
基于這個結構,我們看看SOAP(over http)的Request和Response的樣子:
關于SOAP協議的更多細節,可以參見SOAP協議規范,這里限于篇幅就不細說了。
二.環境準備本文中使用的Go語言版本為go 1.11.2。
獲取wsdl文件
現在在互聯網上要找到一個面向公共的、免費的SOAP服務著實困難。free-web-services.com上的很多服務已經不提供SOAP服務了,并且多數提供SOAP的服務也已經打不開頁面了。在本文中,我們將使用www.dneonline.com/calculator.asmx這個calculator服務,至少目前它還是ready的(不過也不保證它在將來能一直ready)。
我們可以通過下面命令獲得這個calculator服務的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>
這個calculator.wsdl是后續實現soap client和soap server的基礎。
2. 根據wsdl文件生成SOAP package
雖然Go語言標準庫中擁有比較完善的XML操作package,但是我們也沒有必要從頭開始進行SOAP協議的封裝和解包。github上面的gowsdl項目可以幫助我們基于calculator.wsdl自動生成實現SOAP Client和SOAP Server所要使用的各種方法和結構體,這也是我們后續實現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根據calculator.wsdl生成了myservice.go,所有有關calculator soap service的結構體和方法都在這個Go源文件中。有了這個package,我們就可以來實現soap客戶端了。
三.實現SOAP客戶端我們實現一個SOAP客戶端,用于調用www.dneonline.com/calculator服務中提供的Add方法來進行加法計算。
我們先在$GOPATH/src/github.com/bigwhite/experiments/go-soap下面建立client目錄,進入client目錄,創建client的main.go文件。
在前面根據calculator.wsdl生成的myservice.go文件中,我們找到了NewCalculatorSoap方法,該方法會返回一個到對應服務的client實例,通過該soap client實例,我們可以調用其包含的Add方法,我們來看一下main.go中的代碼實現:
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方法的參數為soap.Add結構體的實例,Add結構有兩個字段IntA和IntB,分別代表了兩個加數。我們來執行一下該client實現:
$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輸出了加法服務調用后的正確結果:5。
四.實現SOAP服務下面我們再來實現一個類似www.dneonline.com/calculator的服務,由于只是demo,我們只實現Add方法,其他方法的“套路”是一樣的。
我們在$GOPATH/src/github.com/bigwhite/experiments/go-soap下面建立server目錄,進入server目錄,創建server的main.go文件。pkg/myservice/myservice.go中只是SOAP層協議數據負荷的marshal和unmarshal操作,并沒有網絡層面的支持,因此我們需要自己建立http server框架,我們就使用Go標準庫http server。代碼結構如下:
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) {
? ... ...
}
這個SOAP server的外層結構與普通http server并無太多差異。我們重點要關注的是soapHandler的實現邏輯。
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中讀取出原始數據;
接下來,我們通過一個正則表達式去匹配原始數據,如果匹配到方法,則進入方法的處理函數processAdd;否則提示方法不存在;
最后將processAdd的返回結果marshall為SOAP格式后,返回給client端。
processAdd是真正執行服務算法的函數:
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到一個SOAPEnvelope結構體中,從而得到SOAP envelope中Body中的方法的輸入參數的值:IntA和IntB。將兩個加數的和賦值給AddResult,作為AddResponse的值返回。
我們啟動一下該SOAP server,并修改一下前面client所要連接的soap server的地址,并讓client向我們自己實現的soap server發起服務請求調用:
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.啟動soap server
$GOPATH/src/github.com/bigwhite/experiments/go-soap/server git:(master) ? $go run main.go3. 啟動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成功調用了我們實現的SOAP Server的服務方法,并獲得了正確的結果。一個簡單的SOAP server就這么實現了。不過明眼的朋友肯定已經看出代碼中的問題了,那就是method match那塊僅僅適用于demo,在真正的服務中,服務會有很多method,我們需要一種規范的、通用的匹配機制,一種可以通過SOAP Body匹配來做,另一種可以通過http header中的SOAP Action 來匹配(僅適用于SOAP 1.1,SOAP 1.2版本去掉了SOAP Action)。這里就留給大家自己去發揮吧。
五.小結如果不是碰到了基于SOAP的遺留系統,我想我是不會研究SOAP的,畢竟基于SOAP的系統正在逐漸消逝在大眾的視野中。上面的demo應該可以讓大家對如何用Go與SOAP系統交互有了一個粗淺的認識。這里算是一個不錯的起點。如果大家對于Go與SOAP有更深刻地研究或者有更好的有關SOAP的開源項目,歡迎交流。
文中源碼在這里https://github.com/bigwhite/experiments/tree/master/go-soap可以找到。
我的網課“Kubernetes實戰:高可用集群搭建、配置、運維與應用”在慕課網上線了,感謝小伙伴們學習支持!
我要發短信:企業級短信平臺定制開發專家 https://51smspush.com/
smspush : 可部署在企業內部的定制化短信平臺,三網覆蓋,不懼大并發接入,可定制擴展; 短信內容你來定,不再受約束, 接口豐富,支持長短信,簽名可選。
著名云主機服務廠商DigitalOcean發布最新的主機計劃,入門級Droplet配置升級為:1 core CPU、1G內存、25G高速SSD,價格5$/月。有使用DigitalOcean需求的朋友,可以打開這個鏈接地址:https://m.do.co/c/bff6eed92687 開啟你的DO主機之路。
我的聯系方式:
微博:https://weibo.com/bigwhite20xx
微信公眾號:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite
商務合作方式:撰稿、出書、培訓、在線課程、合伙創業、咨詢、廣告合作。
? 2019, bigwhite. 版權所有.
總結
以上是生活随笔為你收集整理的soap协议_Go和SOAP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 3 5的值_python3
- 下一篇: python绘图教程_pyplot绘图教