web-dev-qa-db-ja.com

GoでのWSDL / SOAPサポート?

GoでSOAP/WSDLをサポートするパッケージはありますか?

19
venkyk

いいえ。

SOAPはひどいですが、SOAPを使用する定義済みのプロトコルのサーバーを実装する必要があったため、net/httpでリッスンし、encoding/xmlでエンベロープをデコード/エンコードしました。数分で、私はGoで最初の封筒をすでに出していました。

12
Moshe Revah

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{}をサポートしていません。

ご覧のとおり、これは複雑になります。統合するAPIが1つある場合SOAP API)、これはおそらくそれほど悪くはないでしょう。それぞれに独自の癖がある複数の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.Attr{xml.Name{"xmlns", "xsi"}, "http://www.w3.org/2001/XMLSchema-instance"},
            xml.Attr{xml.Name{"xmlns", "xsd"}, "http://www.w3.org/2001/XMLSchema"},
            xml.Attr{xml.Name{"xmlns", "soap"}, "http://www.w3.org/2003/05/soap-envelope"},
        })
    x.RegisterTypeMore("", xml.Name{}, []xml.Attr{
        xml.Attr{xml.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 := &Envelope{Body{MethodCall{
        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メソッドを使用して応答オブジェクトまたはFaultを取得します。これは厳密には必要ありません。次のようにすることもできます:

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>

これは、Microsoft .NETの一部であるDiffGramです。 Findメソッドを使用してTable1にアクセスできます。 DecodeおよびDecodeElementメソッドはスライスでも機能します。したがって、NewDataSetに複数の結果が含まれている場合は、[]MethodCallResponseを渡すことができます。

SOAPはダメだというZippowerに同意します。しかし、残念ながら多くの企業がSOAPを使用しており、これらのAPIを使用せざるを得ない場合があります。xmlutilパッケージを使用して少し作成したいと思います。作業するのに苦痛が少ない。

18
Luke

Go自体にはまだ何もありませんが、 gowsdl があります。これまでのところ、いくつかのSOAPサービスとインターフェースをとることは、私にとっては十分に機能しているようです。

私はそれが提供するSOAPプロキシを使用しません。これは認証をサポートしていないと思いますが、 gowsdl はWSDLからマーシャリングに必要な構造体とコードを生成しますリクエストとアンマーシャリングレスポンス-大きな勝利。

5
user3088543

wsdl-go もあります。
でも使ったことがないので、本当に言えません。

0
Stefan Steiger

1つのオプションは、 gsoap を使用することです。これによりC WSDLクライアントが生成され、そのクライアントがGOを介して cgo で使用されます。

0
Edoardo