Go 上的 WSDL/SOAP 支持?
Posted
技术标签:
【中文标题】Go 上的 WSDL/SOAP 支持?【英文标题】:WSDL/SOAP support on Go? 【发布时间】:2012-08-01 21:10:40 【问题描述】:是否有任何软件包可以在 Go 上支持 SOAP/WSDL?
【问题讨论】:
感谢您提供的小型 XML/SOAP 包,做得很好。伙计们可以在这里找到这个库 -- github.com/webconnex/xmlutil 我经常不得不在遗留系统上使用 SOAP,并且直到上周末之前一直在 Go 中通过硬编码结构来实现。破解了 WSDL 的解析器 + Go 代码生成器,可以生成可用的 Go 代码来调用 SOAP。我使用的一些 API 非常广泛,可以生成超过 2k LOC 的文件。看看:github.com/fiorix/wsdl2go 【参考方案1】:Go 中不支持 WSDL。其他语言的支持要么是静态的,要么是动态的:要么是从 WSDL 预先生成的结构,要么是使用哈希表动态完成的。
但是,您可以手动编码和解码 SOAP 请求。我发现标准的encoding/xml
包对于SOAP 来说是不够的。不同的服务器有很多怪癖,encoding/xml
的限制使得很难生成这些服务器满意的请求。
例如,一些服务器在每个字符串标签上都需要xsi:type="xsd:string"
。为了正确执行此操作,您的结构需要看起来像 encoding/xml
:
type MethodCall struct
One XSI
Two XSI
type XSI struct
Type string `xml:"xsi:type,attr"`
Vaue string `xml:",chardata"`
然后你像这样构造它:
MethodCall
XSI"xsd:string", "One",
XSI"xsd:string", "Two",
这给了你:
<MethodCall>
<One xsi:type="xsd:string">One</One>
<Two xsi:type="xsd:string">Two</Two>
</MethodCall>
现在这可能没问题。它当然可以完成工作。但是,如果您需要的不仅仅是string
,该怎么办? encoding/xml
目前不支持interface
。
如您所见,这变得很复杂。如果您有一个要集成的 SOAP API,这可能不会太糟糕。如果你有几个,每个都有自己的怪癖怎么办?
如果你能做到这一点不是很好吗?
type MethodCall struct
One string
Two string
然后对encoding/xml
说:“此服务器需要 xsi 类型”。
为了解决这个问题,我创建了github.com/webconnex/xmlutil。这是一项正在进行的工作。它没有encoding/xml
的编码器/解码器的所有功能,但它具有SOAP 所需的功能。
这是一个工作示例:
package main
import (
"bytes"
"encoding/xml"
"fmt"
"github.com/webconnex/xmlutil"
"log"
//"net/http"
)
type Envelope struct
Body `xml:"soap:"`
type Body struct
Msg interface
type MethodCall struct
One string
Two string
type MethodCallResponse struct
Three string
func main()
x := xmlutil.NewXmlUtil()
x.RegisterNamespace("http://www.w3.org/2001/XMLSchema-instance", "xsi")
x.RegisterNamespace("http://www.w3.org/2001/XMLSchema", "xsd")
x.RegisterNamespace("http://www.w3.org/2003/05/soap-envelope", "soap")
x.RegisterTypeMore(Envelope, xml.Name"http://www.w3.org/2003/05/soap-envelope", "",
[]xml.Attr
xml.Attrxml.Name"xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance",
xml.Attrxml.Name"xmlns", "xsd", "http://www.w3.org/2001/XMLSchema",
xml.Attrxml.Name"xmlns", "soap", "http://www.w3.org/2003/05/soap-envelope",
)
x.RegisterTypeMore("", xml.Name, []xml.Attr
xml.Attrxml.Name"http://www.w3.org/2001/XMLSchema-instance", "type", "xsd:string",
)
buf := new(bytes.Buffer)
buf.WriteString(`<?xml version="1.0" encoding="utf-8"?>`)
buf.WriteByte('\n')
enc := x.NewEncoder(buf)
env := &EnvelopeBodyMethodCall
One: "one",
Two: "two",
if err := enc.Encode(env); err != nil
log.Fatal(err)
// Print request
bs := buf.Bytes()
bs = bytes.Replace(bs, []byte'>', '<', []byte'>', '\n', '<', -1)
fmt.Printf("%s\n\n", bs)
/*
// Send response, SOAP 1.2, fill in url, namespace, and action
var r *http.Response
if r, err = http.Post(url, "application/soap+xml; charset=utf-8; action="+namespace+"/"+action, buf); err != nil
return
dec := x.NewDecoder(r.Body)
*/
// Decode response
dec := x.NewDecoder(bytes.NewBufferString(`<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<soap:Body>
<MethodCallResponse>
<Three>three</Three>
</MethodCallResponse>
</soap:Body>
</soap:Envelope>`))
find := []xml.Name
xml.Name"", "MethodCallResponse",
xml.Name"http://www.w3.org/2003/05/soap-envelope", "Fault",
var start *xml.StartElement
var err error
if start, err = dec.Find(find); err != nil
log.Fatal(err)
if start.Name.Local == "Fault"
log.Fatal("Fault!") // Here you can decode a Soap Fault
var resp MethodCallResponse
if err := dec.DecodeElement(&resp, start); err != nil
log.Fatal(err)
fmt.Printf("%#v\n\n", resp)
在上面的示例中,我使用Find
方法来获取响应对象或故障。这不是绝对必要的。你也可以这样做:
x.RegisterType(MethodCallResponse)
...
// Decode response
dec := x.NewDecoder(bytes.NewBufferString(`<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<soap:Body>
<MethodCallResponse>
<Three>three</Three>
</MethodCallResponse>
</soap:Body>
</soap:Envelope>`))
var start *xml.StartElement
var resp Envelope
if err := dec.DecodeElement(&resp, start); err != nil
log.Fatal(err)
fmt.Printf("%#v\n\n", resp)
当您的数据如下所示时,您会发现 Find
方法很有用:
<soap:Envelope>
<soap:Body>
<MethodResponse>
<MethodResult>
<diffgr:diffgram>
<NewDataSet>
<Table1 diffgr:id="Table1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<Three>three</Three>
</Table1>
</NewDataSet>
</diffgr:diffgram>
</MethodResult>
</MethodResponse>
</soap:Body>
</soap:Envelope>
这是一个 DiffGram,是 Microsoft .NET 的一部分。您可以使用Find
方法访问Table1
。 Decode
和 DecodeElement
方法也适用于切片。因此,如果 NewDataSet
恰好包含多个结果,您可以传入 []MethodCallResponse
。
我同意 Zippower 的观点,即 SOAP 确实很糟糕。但不幸的是,很多企业都在使用 SOAP,有时你不得不使用这些 API。有了 xmlutil 包,我希望使用起来不会那么痛苦。
【讨论】:
Go tip 现在支持 encoding/xml 中的 Marsalers 和 Unmarshalers 就像 encoding/json 一样,这个功能预计会出现在 Go 1.2 中。这可能有助于处理 SOAP。 工作得很好@luke,你的小型 xml 实用程序 lib...js 需要一个入门文档,尽管通过上面的脚本扫描了几分钟,我能够理解如何去做 我写了一个WSDL解析器,可以生成Go代码来调用SOAP。 encoding/xml 在标签方面有很多限制,我在自述文件中放了一个链接。看看:github.com/fiorix/wsdl2go【参考方案2】:没有。
SOAP 很糟糕,但我必须实现一个使用 SOAP 的已定义协议的服务器,所以我用net/http
监听并用encoding/xml
解码/编码信封。几分钟后,我已经用 Go 送达了我的第一个信封。
【讨论】:
当然可以,但是有无数只支持 SOAP 的企业系统。对于这些情况,我们仍然需要一些有用的东西。【参考方案3】:虽然 Go 本身什么都没有,但有 gowsdl。到目前为止,它似乎足以让我与几个 SOAP 服务交互。
我不使用它提供的 SOAP 代理,我相信它不支持身份验证,但 gowsdl 会从 WSDL 生成我需要的结构和代码来编组请求和解组响应——这是一个巨大的胜利。
【讨论】:
【参考方案4】:还有wsdl-go. 但是我没用过,所以真的不能说。
【讨论】:
这似乎不再存在。【参考方案5】:一种选择是使用gsoap,它会生成一个 C WSDL 客户端 然后通过 GO 和 cgo 使用该客户端
【讨论】:
我会重新考虑“最佳选择”!这是一种复杂而令人困惑的方法 gsoap 可能是最稳定和最完整的可用肥皂实现:如果您可以解决从 Go 中使用 C/C++ 的麻烦......那么它就是通往 Go 的方式 :) 没有双关语跨度> 从 C++ 处理 gSoap 我强烈建议不要这样做。 gSoap 代码是我见过的最糟糕的代码之一(其中包括来自硬件供应商的代码!)。这太可怕了。不要使用它。每当您面临其中一个选项是 gSoap 的选择时,请选择另一个选项。即使那个选项是“自己写”。 @eddyce 我很认真。我会告诉你去看看代码,但你看起来是个好人,所以不要对自己这样做。 gsoap 问题的一个例子是它根本没有对其代码进行分层。在soap库中,我希望看到一个用于http的层,一个用于xml,一个用于soap(或用于处理此问题的外部库)。 gsoap 混合了所有这些。没有分层。 @KristofProvost 我从不这样做(看看我的意思的代码)。 wsdl 客户端应该创建有效的代码,它不必很好或可读,因为我什至永远不会打开它。我在我的答案中放弃了“最佳选项”,因为你不是唯一一个这样想的人,另一方面,我已经看到 gsoap 在非常复杂的 wsdl 上提供了一个很好的界面,如果我愿意的话,我会再次使用它。以上是关于Go 上的 WSDL/SOAP 支持?的主要内容,如果未能解决你的问题,请参考以下文章
WSDL SOAP Webservice Java 客户端的错误 415
NODEJS / PHP WSDL SOAP:未将对象引用设置为对象的实例