gosoap

repository·master·Indexed 19 days ago

https://github.com/tiaguinho/gosoap

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

Tokens
4.3K
Snippets
20
Records
21
Agent score
69%

What's inside gosoap

  1. Configure Client Authentication and WSDL Refresh

    master

    The 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).
  2. Initialize a SOAP Client

    master

    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)
  3. Make a basic SOAP call with SoapClient

    master

    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)
    }
  4. Set custom SOAP envelope attributes

    master

    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/",
    })
  5. Set SOAP Header parameters

    master

    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",
    		},
    	},
    }
  6. Retrieve SOAP Action from WSDL operations

    master

    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
    }
  7. Call a SOAP method using a struct

    master

    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},
    })
  8. Unmarshal a SOAP response body

    master

    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)
    }
  9. Handle errors with request payloads

    master

    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)
    }
  10. Handle SOAP Fault errors

    master

    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)
        }
    }
  11. Call a SOAP method with parameters

    master

    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)