2012-08-01 650 views
16

Miré a mi alrededor y todo lo que pude encontrar es la compatibilidad con xml.Soporte WSDL/SOAP en Go?

¿Hay paquetes para admitir SOAP/WSDL en Go?

+0

Gracias por el pequeño paquete XML/SOAP, hizo bien el trabajo. Los chicos pueden encontrar la lib aquí - https://github.com/webconnex/xmlutil – Knights

+0

A menudo tengo que usar SOAP en sistemas heredados, y lo he estado haciendo mediante estructuras de codificación en Go hasta el fin de semana pasado. Hackeado un generador de código analizador + Go para WSDL que puede producir código Go utilizable para llamar a SOAP. Algunas de las API que uso son bastante extensas, generando archivos con más de 2k LOC. Échale un vistazo: https://github.com/fiorix/wsdl2go – fiorix

Respuesta

11

Nope.

SOAP es una mierda, pero tuve que implementar un servidor de un protocolo ya definido que usa SOAP, así que escuché con net/http y sobres descodificados/codificados con encoding/xml. En pocos minutos, ya entregué mi primer sobre con Go.

+1

Claro que sí, pero hay una infinidad de sistemas empresariales que solo admiten SOAP. Para esos casos, todavía necesitamos algo útil. – fiorix

17

No hay soporte para WSDL en Go. El soporte en otros idiomas es estático o dinámico: cualquiera de las estructuras se genera previamente a partir del WSDL, o se hace sobre la marcha con tablas hash.

Sin embargo, puede codificar y decodificar solicitudes SOAP manualmente. Descubrí que el paquete estándar encoding/xml es insuficiente para SOAP. Hay tantos caprichos en servidores diferentes, y las limitaciones en encoding/xml hacen que sea difícil generar una solicitud con la que estos servidores estén satisfechos. Por ejemplo, algunos servidores necesitan xsi:type="xsd:string" en cada etiqueta de cadena. Con el fin de hacer esto correctamente su estructura debe tener este aspecto para encoding/xml:

type MethodCall struct { 
    One XSI 
    Two XSI 
} 

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

Y se construye de esta manera:

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

que le ofrece:

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

Ahora bien, este podría estar bien. Sin duda hace el trabajo. Pero, ¿y si necesitaras algo más que un string? encoding/xml actualmente no es compatible con interface{}.

Como puede ver, esto se complica. Si tuviera una API SOAP para integrar, probablemente no sería tan malo. ¿Qué pasaría si tuviera varios, cada uno con sus propios caprichos?

¿No sería lindo si pudieras hacer esto?

type MethodCall struct { 
    One string 
    Two string 
} 

Entonces dicen a encoding/xml: "Este servidor quiere tipos xsi".

Para resolver este problema creé github.com/webconnex/xmlutil. Es un trabajo en progreso. No tiene todas las características del codificador/decodificador encoding/xml, pero tiene lo que se necesita para SOAP.

Aquí está un ejemplo de trabajo:

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 el ejemplo anterior utilizo el método Find para obtener el objeto respuesta, o un fallo. Esto no es estrictamente necesario. También puede hacerlo de esta manera:

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) 

Encontrará el método Find útil cuando los datos se ve así:

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

Este es un DiffGram, parte de Microsoft .NET.Puede usar el método Find para obtener Table1. El método Decode y DecodeElement también funciona en sectores. De modo que puede pasar un []MethodCallResponse si NewDataSet contiene más de un resultado.

Estoy de acuerdo con Zippower que SOAP apesta. Desafortunadamente, muchas empresas usan SOAP, y algunas veces se ve obligado a utilizar estas API. Con el paquete xmlutil espero que sea un poco menos doloroso trabajar con él.

+6

Go tip ahora es compatible con [Marsalers] (http://tip.golang.org/pkg/encoding/xml/#Marshaler) y [Unmarshalers] (http://tip.golang.org/pkg/encoding/xml/# Unmarshaler) en codificación/xml como encoding/json ya lo hizo, y se espera que esta característica esté en Go 1.2. Esto podría ayudar con el manejo de SOAP. – Matt

+0

Funciona muy bien @luke, su pequeña utilidad xml lib ... js necesita una documentación de inicio, aunque con unos minutos de escaneo a través del script anterior pude entender cómo hacer las cosas – Knights

+0

Escribí un analizador WSDL que puede generar código Go para llamar a SOAP. Hay muchas limitaciones en la codificación/xml con respecto a las etiquetas, y puse un enlace a eso en el archivo README. Échale un vistazo: https://github.com/fiorix/wsdl2go – fiorix

-1

mejor opción es utilizar gsoap que produce un cliente C WSDL y luego usar ese cliente a través de GO con cgo

+1

¡Volvería a considerar la "mejor opción"! Es un enfoque complejo y confuso – Arman

+0

gsoap es probablemente la implementación de jabón más estable y completa disponible: si puede resolver la molestia de usar C/C++ desde Go ... entonces es el camino a Go :) sin juego de palabras – eddyce

5

Si bien todavía no hay nada en sí mismo Go, hay gowsdl. Hasta ahora, parece funcionar lo suficientemente bien como para interactuar con varios servicios SOAP.

No utilizo el proxy SOAP que proporciona, que creo que no es compatible con auth, pero gowsdl genera las estructuras y el código que necesito del WSDL para ordenar las solicitudes y las respuestas unmarshal, una gran ganancia.

0

También hay wsdl-go.
Pero no lo he usado, entonces realmente no puedo decirlo.

+0

This parece que ya no existe. –