Install gosoap via go get
masterTo add gosoap to your Go project, use the following command:
go get github.com/tiaguinho/gosoaprepository·master·Indexed 19 days ago
https://github.com/tiaguinho/gosoapA Go library for SOAP communication that handles XML encoding, SOAP envelope construction, and header/body management. It provides a SoapClient to execute operations via WSDL URLs, supports custom envelope attributes through SetCustomEnvelope, and offers flexible parameter handling via the SoapParams interface (including Params, ArrayParams, and SliceParams). The library includes built-in support for Basic Authentication, SOAP Fault error handling, and WSDL definition parsing.
To add gosoap to your Go project, use the following command:
go get github.com/tiaguinho/gosoapThe Client struct provides several fields for managing the connection lifecycle:
Username & Password: Set these to enable Basic Authentication for all requests.AutoAction: If set to true, the client will attempt to construct the SOAPAction header automatically if it cannot be found in the WSDL.RefreshDefinitionsAfter: A time.Duration that controls how often the WSDL definitions are re-fetched. Note: To prevent abuse, this must be at least 15 minutes (15 * time.Minute).To interact with a SOAP service, you must first create a *Client using a WSDL URL. You can use SoapClient for default settings or SoapClientWithConfig to provide a custom *http.Client and *Config.
Configuration Options:
Dump: A boolean to enable request/response dumping.Logger: An implementation of the DumpLogger interface to handle logged data.If httpClient is nil, a default http.Client is used. If config.Logger is nil, a fmtLogger (which prints to stdout) is used.
// Using default configuration
client, err := gosoap.SoapClient("http://example.com/service?wsdl", nil)
// Using custom configuration
config := &gosoap.Config{
Dump: true,
Logger: myCustomLogger,
}
client, err := gosoap.SoapClientWithConfig("http://example.com/service?wsdl", myHTTPClient, config)To use gosoap, initialize a client using gosoap.SoapClient by providing the WSDL URL and an *http.Client. You can then use the .Call(methodName, params) method to execute a SOAP operation. Use gosoap.Params (a map) to pass parameters and call .Unmarshal(target) on the response to populate your local structs.
Note: If the SOAP response contains an XML string within a field, you may need to perform a second xml.Unmarshal on that specific field.
package main
import (
"encoding/xml"
"log"
"net/http"
"time"
"github.com/tiaguinho/gosoap"
)
// GetIPLocationResponse will hold the Soap response
type GetIPLocationResponse struct {
GetIPLocationResult string `xml:"GetIpLocationResult"`
}
// GetIPLocationResult will
type GetIPLocationResult struct {
XMLName xml.Name `xml:"GeoIP"`
Country string `xml:"Country"`
State string `xml:"State"`
}
var (
r GetIPLocationResponse
)
func main() {
httpClient := &http.Client{
Timeout: 1500 * time.Millisecond,
}
soap, err := gosoap.SoapClient("http://wsgeoip.lavasoft.com/ipservice.asmx?WSDL", httpClient)
if err != nil {
log.Fatalf("SoapClient error: %s", err)
}
// Use gosoap.Params to pass parameters
params := gosoap.Params{
"sIp": "8.8.8.8",
}
res, err := soap.Call("GetIpLocation", params)
if err != nil {
log.Fatalf("Call error: %s", err)
}
res.Unmarshal(&r)
// GetIPLocationResult will be a string. We need to parse it to XML
result := GetIPLocationResult{}
err = xml.Unmarshal([]byte(r.GetIPLocationResult), &result)
if err != nil {
log.Fatalf("xml.Unmarshal error: %s", err)
}
if result.Country != "US" {
log.Fatalf("error: %+v", r)
}
log.Println("Country: ", result.Country)
log.Println("State: ", result.State)
}If your SOAP service requires specific namespace declarations or attributes in the SOAP envelope, use gosoap.SetCustomEnvelope. This function accepts a prefix and a map of attribute names to values.
gosoap.SetCustomEnvelope("soapenv", map[string]string{
"xmlns:soapenv": "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:tem": "http://tempuri.org/",
})You can add custom headers to your SOAP requests by assigning a gosoap.SliceParams to the HeaderParams field of your SoapClient instance. This allows you to pass complex XML structures (like authentication tokens) as headers.
soap.HeaderParams = gosoap.SliceParams{
xml.StartElement{
Name: xml.Name{
Space: "auth",
Local: "Login",
},
},
"user",
xml.EndElement{
Name: xml.Name{
Space: "auth",
Local: "Login",
},
},
xml.StartElement{
Name: xml.Name{
Space: "auth",
Local: "Password",
},
},
"P@ssw0rd",
xml.EndElement{
Name: xml.Name{
Space: "auth",
Local: "Password",
},
},
}The GetSoapActionFromWsdlOperation method on the *wsdlDefinitions struct allows you to look up the specific soapAction associated with a WSDL operation name. This is useful because the soapAction defined in the binding might differ from the operation's name itself.
Currently, this method checks the first binding (Bindings[0]) in the WSDL definitions to find the matching operation and its corresponding SoapAction.
// Assuming 'wsdl' is a *gosoap.wsdlDefinitions
soapAction := wsdl.GetSoapActionFromWsdlOperation("MyOperationName")
if soapAction != "" {
// Use the retrieved soapAction for the request
}If you prefer working with typed data, use CallByStruct. This requires a RequestStruct (which is a wrapper for your custom struct and the method name). This method internally converts the struct to a request and executes it via Do.
// Note: RequestStruct implementation details are in the package
type MyRequest struct {
ID int `xml:"id"`
}
// Assuming NewRequestByStruct is available
res, err := client.CallByStruct(gosoap.RequestStruct{
Method: "GetItem",
Payload: MyRequest{ID: 123},
})Use the Unmarshal method on a Response object to decode the SOAP body into a target Go struct.
Behavioral Note: The Unmarshal method first attempts to unmarshal the body into a SOAP Fault. If the unmarshaling reveals a SOAP fault (indicated by a non-empty Code), it returns a FaultError instead of populating your target struct. If no fault is present, it unmarshals the body into the provided interface v.
// Assuming 'resp' is a *gosoap.Response and 'target' is your expected struct
err := resp.Unmarshal(&target)
if err != nil {
// Handle error (could be a FaultError or a standard unmarshaling error)
}When a SOAP request fails, gosoap may return an ErrorWithPayload error. This error type wraps the standard error and includes the raw []byte payload that was sent, which is useful for debugging failed XML transmissions.
You can use GetPayloadFromError to extract the payload from an error object.
res, err := client.Call("SomeMethod", params)
if err != nil {
payload := gosoap.GetPayloadFromError(err)
if payload != nil {
fmt.Printf("Failed with payload: %s\n", string(payload))
}
log.Fatal(err)
}When a SOAP service returns a fault, gosoap returns a FaultError. You can detect if an error is specifically a SOAP fault using the IsFault function. This allows you to distinguish between transport/network errors and application-level SOAP faults.
To handle a fault, check the error with IsFault(err) and then typecast it to FaultError to access the underlying fault details.
err := resp.Unmarshal(&target)
if err != nil {
if gosoap.IsFault(err) {
// It is a SOAP Fault
faultErr := err.(gosoap.FaultError)
fmt.Println("SOAP Fault occurred:", faultErr.Error())
} else {
// It is a different kind of error (e.g., network or XML malformation)
fmt.Println("Standard error:", err)
}
}Use the Call method to execute a SOAP operation. It requires the method name (as a string) and SoapParams.
SoapParams is an interface that can be satisfied by several types:
Params: A map[string]interface{} for key-value pairs.HeaderParams: A map[string]interface{} specifically for SOAP headers.ArrayParams: A slice of [2]interface{} (useful for representing XML attributes or specific sequences).SliceParams: A simple []interface{}.The method returns a *Response containing the XML body, headers, and the original payload, or an error.
// Example using map parameters
params := gosoap.Params{
"GetPrice": gosoap.Params{
"ItemCode": "ABC-123",
},
}
res, err := client.Call("GetPrice", params)
if err != nil {
// Handle error
}
// Access response body
fmt.Println(res.Body)