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 方法访问Table1DecodeDecodeElement 方法也适用于切片。因此,如果 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:未将对象引用设置为对象的实例

采用WebService客户端调用WSDL/SOAP网络报错的解决办法

天气查询接口文档

Spark支持通过GO语言编写程序吗

webservice是啥