2012-08-01 14 views
16

Mi sono guardato intorno e tutto quello che ho trovato è il supporto per xml.Supporto WSDL/SOAP su Go?

Esistono pacchetti per supportare SOAP/WSDL su Go?

+0

Grazie per il piccolo pacchetto XML/SOAP, ha funzionato bene. I ragazzi possono trovare la lib qui - https://github.com/webconnex/xmlutil – Knights

+0

Spesso devo usare SOAP su sistemi legacy, e lo sto facendo con strutture hard in Go fino allo scorso fine settimana. Hacked up parser + Go code generator per WSDL che può produrre codice Go utilizzabile per chiamare SOAP. Alcune delle API che uso sono piuttosto estese, generando file con oltre 2k LOC. Scoprilo: https://github.com/fiorix/wsdl2go – fiorix

risposta

11

No.

SOAP succhia, ma ho dovuto implementare un server di un protocollo già definito che utilizza SOAP, quindi ascoltato con net/http e decodificato/buste codificati con encoding/xml. In pochi minuti ho già servito la mia prima busta con Go.

+1

Certo, ma esiste un'infinità di sistemi aziendali che supportano solo SOAP. Per quei casi, abbiamo ancora bisogno di qualcosa di utile. – fiorix

17

Non c'è supporto per WSDL in Go. Il supporto in altre lingue è statico o dinamico: entrambe le strutture sono pre-generate dal WSDL, oppure vengono eseguite al volo con tabelle hash.

È tuttavia possibile codificare e decodificare le richieste SOAP manualmente. Ho trovato che il pacchetto standard encoding/xml non è sufficiente per SOAP. Ci sono così tante stranezze in diversi server, e le limitazioni in encoding/xml rendono difficile generare una richiesta con cui questi server sono soddisfatti.

Ad esempio, alcuni server richiedono xsi:type="xsd:string" su ogni tag stringa. Per farlo correttamente la vostra struct ha bisogno di guardare come questo per encoding/xml:

type MethodCall struct { 
    One XSI 
    Two XSI 
} 

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

E si costruisce in questo modo:

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

che ti dà:

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

Ora questo potrebbe essere ok Sicuramente ottiene il lavoro svolto. Ma se avessi bisogno di più di un semplice string? encoding/xml attualmente non supporta interface{}.

Come potete vedere, questo si complica. Se avevi una API SOAP da integrare, probabilmente non sarebbe male. E se ne avessi diversi, ognuno con le proprie stranezze?

Non sarebbe bello se potessi fare questo?

type MethodCall struct { 
    One string 
    Two string 
} 

Poi dico encoding/xml: "Questo server vuole tipi XSI".

Per risolvere questo problema ho creato github.com/webconnex/xmlutil. E 'un lavoro in corso. Non ha tutte le funzionalità dell'encoder/decoder di encoding/xml, ma ha ciò che è necessario per SOAP.

Ecco un esempio di lavoro:

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

Con l'esempio di cui sopra io uso il metodo Find per ottenere l'oggetto risposta, o un guasto. Questo non è strettamente necessario. È anche possibile fare in questo modo:

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) 

troverete il metodo Find utile quando i dati si presenta così:

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

Questo è un DiffGram, parte di Microsoft .NET.È possibile utilizzare il metodo Find per arrivare a Table1. Il metodo Decode e DecodeElement funziona anche su sezioni. Quindi è possibile passare in un []MethodCallResponse se si verifica che NewDataSet contenga più di un risultato.

Sono d'accordo con Zippower che SOAP fa schifo. Ma sfortunatamente molte aziende usano SOAP e qualche volta sei costretto a usare queste API. Con il pacchetto xmlutil spero di renderlo un po 'meno doloroso con cui lavorare.

+6

Go tip ora supporta [Marsalers] (http://tip.golang.org/pkg/encoding/xml/#Marshaler) e [Unmarshalers] (http://tip.golang.org/pkg/encoding/xml/# Unmarshaler) in codifica/xml come codifica/json già fatto, e questa caratteristica dovrebbe essere in Go 1.2. Questo potrebbe aiutare a gestire SOAP. – Matt

+0

Funziona alla grande @luke, la tua piccola utility xml lib ... js ha bisogno di una documentazione introduttiva, anche se con pochi minuti di scansione attraverso lo script sopra sono stato in grado di capire come andare da cose – Knights

+0

Ho scritto un parser WSDL che può generare un codice Go per chiamare SOAP. Ci sono molte limitazioni nella codifica/xml per quanto riguarda i tag e ho inserito un link nel README. Dai un'occhiata a: https://github.com/fiorix/wsdl2go – fiorix

-1

L'opzione migliore è quella di utilizzare gsoap che produce un client C WSDL e quindi utilizzare tale cliente attraverso GO con cgo

+1

Riconsidererei la "migliore opzione"! È un approccio complesso e confuso – Arman

+0

gsoap è probabilmente l'implementazione di sapone più stabile e completa disponibile: se riesci a risolvere il problema dell'utilizzo di C/C++ da Go ... allora è il modo di andare :) no pun intended – eddyce

5

Mentre non c'è ancora nulla di Go per sé, non v'è gowsdl. Finora, sembra funzionare abbastanza bene per me per interfacciarmi con diversi servizi SOAP.

Non utilizzo il proxy SOAP fornito, che credo non supporti l'autenticazione, ma lo standard gowsdl genera le strutture e il codice necessari dal WSDL alle richieste di marshall e risposte unmarshal: una grande vittoria.

0

C'è anche wsdl-go.
Ma non l'ho usato, quindi non posso davvero dire.

+0

This sembra non esistere più. –