Existe-t-il des packages à prendre en charge SOAP/WSDL on Go?
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.
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.
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.
Il y a aussi wsdl-go .
Mais je ne l'ai pas utilisé, donc je ne peux vraiment pas le dire.