web-dev-qa-db-fra.com

Prise en charge de WSDL/SOAP sur Go?

Existe-t-il des packages à prendre en charge SOAP/WSDL on Go?

18
venkyk

Nan.

SOAP est nul, mais j'ai dû implémenter un serveur d'un protocole déjà défini qui utilise SOAP. J'ai donc écouté avec net/http et décodé/encodé des enveloppes avec encoding/xml. En quelques minutes, j'ai déjà servi ma première enveloppe avec Go.

13
Moshe Revah

Il n'y a pas de support pour WSDL dans Go. La prise en charge dans d'autres langages est statique ou dynamique: les structures sont pré-générées à partir du WSDL ou elles sont réalisées à la volée avec des tables de hachage.

Vous pouvez toutefois encoder et décoder les demandes SOAP manuellement. J'ai trouvé que le paquet standard encoding/xml était insuffisant pour SOAP. Il y a tellement de bizarreries dans différents serveurs, et les limitations de encoding/xml rendent difficile la génération d'une requête qui satisfasse ces serveurs.

Par exemple, certains serveurs ont besoin de xsi:type="xsd:string" sur chaque balise de chaîne. Pour faire cela correctement, votre structure doit ressembler à ceci pour encoding/xml:

type MethodCall struct {
    One XSI
    Two XSI
}

type XSI struct {
    Type string `xml:"xsi:type,attr"`
    Vaue string `xml:",chardata"`
}

Et vous le construisez comme ceci:

MethodCall{
    XSI{"xsd:string", "One"},
    XSI{"xsd:string", "Two"},
}

Ce qui vous donne:

<MethodCall>
    <One xsi:type="xsd:string">One</One>
    <Two xsi:type="xsd:string">Two</Two>
</MethodCall>

Maintenant, ça pourrait aller. Cela fait certainement le travail. Mais que faire si vous aviez besoin de plus que juste une string? encoding/xml ne prend actuellement pas en charge interface{}.

Comme vous pouvez le voir, cela devient compliqué. Si vous deviez intégrer une API SOAP, ce ne serait probablement pas si grave. Et si vous en aviez plusieurs, chacun avec ses propres bizarreries?

Ne serait-il pas agréable si vous pouviez simplement faire cela?

type MethodCall struct {
    One string
    Two string
}

Puis dites à encoding/xml: "Ce serveur veut les types xsi".

Pour résoudre ce problème, j'ai créé github.com/webconnex/xmlutil . C'est un travail en cours. Il ne possède pas toutes les fonctionnalités du codeur/décodeur de encoding/xml, mais il a tout ce qu'il faut pour SOAP.

Voici un exemple de travail:

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)
}

Avec l'exemple ci-dessus, j'utilise la méthode Find pour obtenir l'objet de réponse ou un défaut. Ce n'est pas strictement nécessaire. Vous pouvez aussi le faire comme ça:

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)

Vous trouverez la méthode Find utile lorsque vos données ressemblent à ceci:

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

Ceci est un DiffGram, une partie de Microsoft .NET. Vous pouvez utiliser la méthode Find pour accéder à Table1. Les méthodes Decode et DecodeElement fonctionnent également sur les tranches. Vous pouvez donc passer un []MethodCallResponse si NewDataSet contient plusieurs résultats.

Je suis d'accord avec Zippower pour dire que SOAP craint vraiment. Malheureusement, de nombreuses entreprises utilisent SOAP et vous êtes parfois obligé d’utiliser ces API. Avec le paquetage xmlutil, j'espère rendre le travail moins pénible.

18
Luke

Bien qu'il n'y ait toujours rien dans Go lui-même, il y a gowsdl . Jusqu'ici, cela semble fonctionner assez bien pour que je puisse me connecter à plusieurs services SOAP.

Je n'utilise pas le proxy SOAP fourni, qui, à mon avis, ne prend pas en charge l'auth, mais gowsdl génère les structs et le code dont j'ai besoin du WSDL pour traiter les demandes et libérer les réponses-- une grande victoire.

5
user3088543

Il y a aussi wsdl-go .
Mais je ne l'ai pas utilisé, donc je ne peux vraiment pas le dire.

0
Stefan Steiger

une option consiste à utiliser gsoap qui produit un client C WSDL , puis à utiliser ce client via GO avec cgo

0
Edoardo