Поддержка WSDL/SOAP на Go?

Я огляделся, и все, что я смог найти, это поддержка xml.

Существуют ли какие-либо пакеты для поддержки SOAP/WSDL при Go?

Ответы

Ответ 1

Неа.

SOAP отстой, но мне пришлось реализовать сервер уже определенного протокола, который использует SOAP, поэтому я слушал с net/http и декодированными/закодированными конвертами с encoding/xml. Через несколько минут я уже подал свой первый конверт с Go.

Ответ 2

В 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.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>

Это DiffGram, часть Microsoft.NET. Вы можете использовать метод Find, чтобы добраться до Table1. Метод Decode и DecodeElement также работает на срезах. Таким образом, вы можете передать []MethodCallResponse, если NewDataSet содержит более одного результата.

Я согласен с Zippower, что SOAP действительно сосать. Но, к сожалению, многие предприятия используют SOAP, и иногда вам приходится использовать эти API. С пакетом xmlutil я надеюсь сделать его немного менее болезненным для работы.

Ответ 3

Пока в самом Go нет ничего, есть gowsdl. Пока что, похоже, для меня достаточно хорошо взаимодействовать с несколькими службами SOAP.

Я не использую предоставленный им SOAP-прокси, который, я считаю, не поддерживает auth, но gowsdl генерирует структуры и код, которые мне нужны из WSDL, для маршалирования запросов и немедленных ответов - большой выигрыш.

Ответ 4

Существует также wsdl-go.
Но я не использовал его, поэтому я действительно не могу сказать.

Ответ 5

Лучший вариант - использовать gsoap, который создает клиент WSDL и затем используйте этот клиент через GO с cgo