内容简介:在Go语言诞生后,主流的Web Service设计已经开始过渡到REST和RPC,Go相关开源项目也以对REST和RPC的支持为主。而对SOAP的支持则少而零散,社区里也没有对SOAP支持的重量级开源项目,在但Gopher世界还是有以client身份与SOAP service交互或是实现SOAP server的需求的。在这篇文章中,我就和大家一起来探索一下如何基于一些开源项目,使用Go实现SOAP client和SOAP Server的。
在 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。
1. 获取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"?> <wsdl: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/"> ... ... <wsdl:service name="Calculator"> <wsdl:port name="CalculatorSoap" binding="tns:CalculatorSoap"> <soap:address location="http://www.dneonline.com/calculator.asmx" /> </wsdl:port> <wsdl:port name="CalculatorSoap12" binding="tns:CalculatorSoap12"> <soap12:address location="http://www.dneonline.com/calculator.asmx" /> </wsdl:port> </wsdl:service> </wsdl: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 main import ( "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.go 2019/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>2</intA><intB>3</intB></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>5</AddResult></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 main import ( "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(`<Add xmlns=`) 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.go 3. 启动client $go run main.go 2019/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>2</intA><intB>3</intB></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>5</AddResult> </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的开源项目,欢迎交流。
文中源码在 这里 可以找到。
我的网课“ 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. 版权所有.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Mechanics of Web Handling
David R. Roisum
This unique book covers many aspects of web handling for manufacturing, converting, and printing. The book is applicable to any web including paper, film, foil, nonwovens, and textiles. The Mech......一起来看看 《The Mechanics of Web Handling》 这本书的介绍吧!
XML、JSON 在线转换
在线XML、JSON转换工具
HEX HSV 转换工具
HEX HSV 互换工具