mirror of
https://github.com/foomo/soap.git
synced 2025-10-16 12:45:36 +00:00
added custom marshalling, verbosity and cleaned up
This commit is contained in:
parent
f2772cc8a7
commit
6f5b9d46a0
66
README.md
66
README.md
@ -4,29 +4,45 @@ First of all do not write SOAP services if you can avoid it! It is over.
|
|||||||
|
|
||||||
If you can not avoid it this package might help.
|
If you can not avoid it this package might help.
|
||||||
|
|
||||||
|
## Service
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/foomo/soap"
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/foomo/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FooRequest a simple request
|
||||||
type FooRequest struct {
|
type FooRequest struct {
|
||||||
Foo string
|
XMLName xml.Name `xml:"fooRequest"`
|
||||||
|
Foo string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FooResponse a simple response
|
||||||
type FooResponse struct {
|
type FooResponse struct {
|
||||||
Bar string
|
Bar string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunServer run a little demo server
|
||||||
func RunServer() {
|
func RunServer() {
|
||||||
soapServer := soap.NewServer("127.0.0.1:8080")
|
soapServer := soap.NewServer()
|
||||||
|
|
||||||
soapServer.HandleOperation(
|
soapServer.HandleOperation(
|
||||||
|
// SOAPAction
|
||||||
"operationFoo",
|
"operationFoo",
|
||||||
|
// tagname of soap body content
|
||||||
|
"fooRequest",
|
||||||
|
// RequestFactoryFunc - give the server sth. to unmarshal the request into
|
||||||
func() interface{} {
|
func() interface{} {
|
||||||
return &FooRequest{}
|
return &FooRequest{}
|
||||||
},
|
},
|
||||||
func(request interface{}) (response interface{}, err error) {
|
// OperationHandlerFunc - do something
|
||||||
fooRequest := request.(FooRequest)
|
func(request interface{}, w http.ResponseWriter, httpRequest *http.Request) (response interface{}, err error) {
|
||||||
|
fooRequest := request.(*FooRequest)
|
||||||
fooResponse := &FooResponse{
|
fooResponse := &FooResponse{
|
||||||
Bar: "Hello " + fooRequest.Foo,
|
Bar: "Hello " + fooRequest.Foo,
|
||||||
}
|
}
|
||||||
@ -34,11 +50,47 @@ func RunServer() {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
err := soapServer.ListenAndServe(":8080")
|
||||||
|
fmt.Println("exiting with error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
RunServer()
|
RunServer()
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/foomo/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FooRequest a simple request
|
||||||
|
type FooRequest struct {
|
||||||
|
XMLName xml.Name `xml:"fooRequest"`
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooResponse a simple response
|
||||||
|
type FooResponse struct {
|
||||||
|
Bar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
soap.Verbose = true
|
||||||
|
client := soap.NewClient("http://127.0.0.1:8080/", nil, nil)
|
||||||
|
response := &FooResponse{}
|
||||||
|
httpResponse, err := client.Call("operationFoo", &FooRequest{Foo: "hello i am foo"}, response)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
log.Println(response.Bar, httpResponse.Status)
|
||||||
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
140
client.go
Normal file
140
client.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package soap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientDialTimeout default timeout 30s
|
||||||
|
var ClientDialTimeout = time.Duration(30 * time.Second)
|
||||||
|
|
||||||
|
// UserAgent is the default user agent
|
||||||
|
var UserAgent = "go-soap-0.1"
|
||||||
|
|
||||||
|
// Verbose be verbose
|
||||||
|
var Verbose = false
|
||||||
|
|
||||||
|
func l(m ...interface{}) {
|
||||||
|
if Verbose {
|
||||||
|
log.Println(m...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XMLMarshaller lets you inject your favourite custom xml implementation
|
||||||
|
type XMLMarshaller interface {
|
||||||
|
Marshal(v interface{}) ([]byte, error)
|
||||||
|
Unmarshal(xml []byte, v interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultMarshaller struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *defaultMarshaller) Marshal(v interface{}) (xmlBytes []byte, err error) {
|
||||||
|
return xml.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *defaultMarshaller) Unmarshal(xmlBytes []byte, v interface{}) error {
|
||||||
|
return xml.Unmarshal(xmlBytes, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultMarshaller() XMLMarshaller {
|
||||||
|
return &defaultMarshaller{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialTimeout(network, addr string) (net.Conn, error) {
|
||||||
|
return net.DialTimeout(network, addr, ClientDialTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicAuth credentials for the client
|
||||||
|
type BasicAuth struct {
|
||||||
|
Login string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client generic SOAP client
|
||||||
|
type Client struct {
|
||||||
|
url string
|
||||||
|
tls bool
|
||||||
|
auth *BasicAuth
|
||||||
|
tr *http.Transport
|
||||||
|
Marshaller XMLMarshaller
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient constructor
|
||||||
|
func NewClient(url string, auth *BasicAuth, tr *http.Transport) *Client {
|
||||||
|
return &Client{
|
||||||
|
url: url,
|
||||||
|
auth: auth,
|
||||||
|
tr: tr,
|
||||||
|
Marshaller: newDefaultMarshaller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call make a SOAP call
|
||||||
|
func (s *Client) Call(soapAction string, request, response interface{}) (httpResponse *http.Response, err error) {
|
||||||
|
envelope := Envelope{}
|
||||||
|
|
||||||
|
envelope.Body.Content = request
|
||||||
|
|
||||||
|
xmlBytes, err := s.Marshaller.Marshal(envelope)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", s.url, bytes.NewBuffer(xmlBytes))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.auth != nil {
|
||||||
|
req.SetBasicAuth(s.auth.Login, s.auth.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", "text/xml; charset=\"utf-8\"")
|
||||||
|
req.Header.Set("User-Agent", UserAgent)
|
||||||
|
|
||||||
|
if soapAction != "" {
|
||||||
|
req.Header.Add("SOAPAction", soapAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Close = true
|
||||||
|
tr := s.tr
|
||||||
|
if tr == nil {
|
||||||
|
tr = http.DefaultTransport.(*http.Transport)
|
||||||
|
}
|
||||||
|
client := &http.Client{Transport: tr}
|
||||||
|
l("POST to", s.url, "with", string(xmlBytes))
|
||||||
|
httpResponse, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer httpResponse.Body.Close()
|
||||||
|
|
||||||
|
rawbody, err := ioutil.ReadAll(httpResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(rawbody) == 0 {
|
||||||
|
l("empty response")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l("response", string(rawbody))
|
||||||
|
respEnvelope := new(Envelope)
|
||||||
|
respEnvelope.Body = Body{Content: response}
|
||||||
|
err = xml.Unmarshal(rawbody, respEnvelope)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fault := respEnvelope.Body.Fault
|
||||||
|
if fault != nil {
|
||||||
|
err = fault
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
30
examples/client.go
Normal file
30
examples/client.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/foomo/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FooRequest a simple request
|
||||||
|
type FooRequest struct {
|
||||||
|
XMLName xml.Name `xml:"fooRequest"`
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooResponse a simple response
|
||||||
|
type FooResponse struct {
|
||||||
|
Bar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
soap.Verbose = true
|
||||||
|
client := soap.NewClient("http://127.0.0.1:8080/", nil, nil)
|
||||||
|
response := &FooResponse{}
|
||||||
|
httpResponse, err := client.Call("operationFoo", &FooRequest{Foo: "hello i am foo"}, response)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
log.Println(response.Bar, httpResponse.Status)
|
||||||
|
}
|
||||||
52
examples/simple-server.go
Normal file
52
examples/simple-server.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/foomo/soap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FooRequest a simple request
|
||||||
|
type FooRequest struct {
|
||||||
|
XMLName xml.Name `xml:"fooRequest"`
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooResponse a simple response
|
||||||
|
type FooResponse struct {
|
||||||
|
Bar string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunServer run a little demo server
|
||||||
|
func RunServer() {
|
||||||
|
soapServer := soap.NewServer()
|
||||||
|
soapServer.HandleOperation(
|
||||||
|
// SOAPAction
|
||||||
|
"operationFoo",
|
||||||
|
// tagname of soap body content
|
||||||
|
"fooRequest",
|
||||||
|
// RequestFactoryFunc - give the server sth. to unmarshal the request into
|
||||||
|
func() interface{} {
|
||||||
|
return &FooRequest{}
|
||||||
|
},
|
||||||
|
// OperationHandlerFunc - do something
|
||||||
|
func(request interface{}, w http.ResponseWriter, httpRequest *http.Request) (response interface{}, err error) {
|
||||||
|
fooRequest := request.(*FooRequest)
|
||||||
|
fooResponse := &FooResponse{
|
||||||
|
Bar: "Hello \"" + fooRequest.Foo + "\"",
|
||||||
|
}
|
||||||
|
response = fooResponse
|
||||||
|
return
|
||||||
|
},
|
||||||
|
)
|
||||||
|
err := soapServer.ListenAndServe(":8080")
|
||||||
|
fmt.Println("exiting with error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// see what is going on
|
||||||
|
soap.Verbose = true
|
||||||
|
RunServer()
|
||||||
|
}
|
||||||
171
server.go
171
server.go
@ -1,71 +1,170 @@
|
|||||||
package soap
|
package soap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OperationHandlerFunc func(request interface{}) (response interface{}, err error)
|
// OperationHandlerFunc runs the actual business logic - request is whatever you constructed in RequestFactoryFunc
|
||||||
|
type OperationHandlerFunc func(request interface{}, w http.ResponseWriter, httpRequest *http.Request) (response interface{}, err error)
|
||||||
|
|
||||||
|
// RequestFactoryFunc constructs a request object for OperationHandlerFunc
|
||||||
type RequestFactoryFunc func() interface{}
|
type RequestFactoryFunc func() interface{}
|
||||||
|
|
||||||
|
type dummyContent struct{}
|
||||||
|
|
||||||
type operationHander struct {
|
type operationHander struct {
|
||||||
requestFactory RequestFactoryFunc
|
requestFactory RequestFactoryFunc
|
||||||
handler OperationHandlerFunc
|
handler OperationHandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type responseWriter struct {
|
||||||
handlers map[string]map[string]*operationHander
|
w http.ResponseWriter
|
||||||
|
outputStarted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Header() http.Header {
|
||||||
|
return w.w.Header()
|
||||||
|
}
|
||||||
|
func (w *responseWriter) Write(b []byte) (int, error) {
|
||||||
|
w.outputStarted = true
|
||||||
|
return w.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) WriteHeader(code int) {
|
||||||
|
w.w.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server a SOAP server, which can be run standalone or used as a http.HandlerFunc
|
||||||
|
type Server struct {
|
||||||
|
handlers map[string]map[string]*operationHander
|
||||||
|
Marshaller XMLMarshaller
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer construct a new SOAP server
|
||||||
func NewServer() *Server {
|
func NewServer() *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
handlers: make(map[string]map[string]*operationHander),
|
handlers: make(map[string]map[string]*operationHander),
|
||||||
|
Marshaller: newDefaultMarshaller(),
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleOperation register to handle an operation
|
// HandleOperation register to handle an operation
|
||||||
func (s *Server) HandleOperation(action string, messageType string, requestFactory RequestFactoryFunc, operationHandlerFunc OperationHandlerFunc) {
|
func (s *Server) HandleOperation(action string, messageType string, requestFactory RequestFactoryFunc, operationHandlerFunc OperationHandlerFunc) {
|
||||||
|
_, ok := s.handlers[action]
|
||||||
|
if !ok {
|
||||||
|
s.handlers[action] = make(map[string]*operationHander)
|
||||||
|
}
|
||||||
s.handlers[action][messageType] = &operationHander{
|
s.handlers[action][messageType] = &operationHander{
|
||||||
handler: operationHandlerFunc,
|
handler: operationHandlerFunc,
|
||||||
requestFactory: requestFactory,
|
requestFactory: requestFactory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) serveSOAP(requestEnvelopeBytes []byte, soapAction string) (responseEnvelopeBytes []byte, err error) {
|
func (s *Server) handleError(err error, w http.ResponseWriter) {
|
||||||
messageType := "find me as the element name in the soap body"
|
// has to write a soap fault
|
||||||
actionHandlers, ok := s.handlers[soapAction]
|
l("handling error:", err)
|
||||||
if !ok {
|
responseEnvelope := &Envelope{
|
||||||
err = errors.New("could not find handlers for action: \"" + soapAction + "\"")
|
Body: Body{
|
||||||
return
|
Content: &Fault{
|
||||||
|
String: err.Error(),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
handler, ok := actionHandlers[messageType]
|
xmlBytes, xmlErr := s.Marshaller.Marshal(responseEnvelope)
|
||||||
if !ok {
|
if xmlErr != nil {
|
||||||
err = errors.New("no handler for message type: " + messageType)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
w.Write([]byte("could not marshal soap fault for: " + err.Error() + " xmlError: " + xmlErr.Error()))
|
||||||
}
|
|
||||||
request := handler.requestFactory()
|
|
||||||
// parse from envelope.body.content into request
|
|
||||||
response, err := handler.handler(request)
|
|
||||||
responseEnvelope := &SOAPEnvelope{
|
|
||||||
Body: SOAPBody{},
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// soap fault party time
|
|
||||||
responseEnvelope.Body.Fault = &SOAPFault{
|
|
||||||
String: err.Error(),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
responseEnvelope.Body.Content = response
|
w.Write(xmlBytes)
|
||||||
}
|
}
|
||||||
// marshal responseEnvelope
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "POST":
|
case "POST":
|
||||||
w.Write([]byte("that actually could be a soap request"))
|
l("incoming POST")
|
||||||
|
soapRequestBytes, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.handleError(errors.New("could not read POST:: "+err.Error()), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
soapAction := r.Header.Get("SOAPAction")
|
||||||
|
l("SOAPAction", "\""+soapAction+"\"")
|
||||||
|
actionHandlers, ok := s.handlers[soapAction]
|
||||||
|
if !ok {
|
||||||
|
s.handleError(errors.New("unknown action \""+soapAction+"\""), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to find out, what is in the body
|
||||||
|
probeEnvelope := &Envelope{
|
||||||
|
Body: Body{
|
||||||
|
Content: &dummyContent{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = xml.Unmarshal(soapRequestBytes, &probeEnvelope)
|
||||||
|
if err != nil {
|
||||||
|
s.handleError(errors.New("could not probe soap body content:: "+err.Error()), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := probeEnvelope.Body.SOAPBodyContentType
|
||||||
|
l("found content type", t)
|
||||||
|
actionHandler, ok := actionHandlers[t]
|
||||||
|
if !ok {
|
||||||
|
s.handleError(errors.New("no action handler for content type: \""+t+"\""), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
request := actionHandler.requestFactory()
|
||||||
|
envelope := &Envelope{
|
||||||
|
Body: Body{
|
||||||
|
Content: request,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = xml.Unmarshal(soapRequestBytes, &envelope)
|
||||||
|
if err != nil {
|
||||||
|
s.handleError(errors.New("could not unmarshal request:: "+err.Error()), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l("request", jsonDump(envelope))
|
||||||
|
|
||||||
|
// we have a valid request time to call the handler
|
||||||
|
responseWriter := &responseWriter{
|
||||||
|
w: w,
|
||||||
|
outputStarted: false,
|
||||||
|
}
|
||||||
|
response, err := actionHandler.handler(request, responseWriter, r)
|
||||||
|
if err != nil {
|
||||||
|
l("action handler threw up")
|
||||||
|
s.handleError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l("result", jsonDump(response))
|
||||||
|
if !responseWriter.outputStarted {
|
||||||
|
responseEnvelope := &Envelope{
|
||||||
|
Body: Body{
|
||||||
|
Content: response,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
xmlBytes, err := s.Marshaller.Marshal(responseEnvelope)
|
||||||
|
if err != nil {
|
||||||
|
s.handleError(errors.New("could not marshal response:: "+err.Error()), w)
|
||||||
|
}
|
||||||
|
w.Write(xmlBytes)
|
||||||
|
} else {
|
||||||
|
l("action handler sent its own output")
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// this will be a soap fault !?
|
// this will be a soap fault !?
|
||||||
w.Write([]byte("this is a soap service - you have to POST soap requests\n"))
|
w.Write([]byte("this is a soap service - you have to POST soap requests\n"))
|
||||||
@ -73,6 +172,18 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func jsonDump(v interface{}) string {
|
||||||
|
if !Verbose {
|
||||||
|
return "not dumping"
|
||||||
|
}
|
||||||
|
jsonBytes, err := json.MarshalIndent(v, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error in json dump :: " + err.Error()
|
||||||
|
}
|
||||||
|
return string(jsonBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe run standalone
|
||||||
func (s *Server) ListenAndServe(addr string) error {
|
func (s *Server) ListenAndServe(addr string) error {
|
||||||
return http.ListenAndServe(addr, s)
|
return http.ListenAndServe(addr, s)
|
||||||
}
|
}
|
||||||
|
|||||||
27
soap.go
27
soap.go
@ -2,26 +2,31 @@ package soap
|
|||||||
|
|
||||||
import "encoding/xml"
|
import "encoding/xml"
|
||||||
|
|
||||||
type SOAPEnvelope struct {
|
// Envelope type
|
||||||
|
type Envelope struct {
|
||||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
|
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
|
||||||
|
|
||||||
Body SOAPBody
|
Body Body
|
||||||
}
|
}
|
||||||
|
|
||||||
type SOAPHeader struct {
|
// Header type
|
||||||
|
type Header struct {
|
||||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"`
|
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"`
|
||||||
|
|
||||||
Header interface{}
|
Header interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SOAPBody struct {
|
// Body type
|
||||||
|
type Body struct {
|
||||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
|
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
|
||||||
|
|
||||||
Fault *SOAPFault `xml:",omitempty"`
|
Fault *Fault `xml:",omitempty"`
|
||||||
Content interface{} `xml:",omitempty"`
|
Content interface{} `xml:",omitempty"`
|
||||||
|
SOAPBodyContentType string `xml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SOAPFault struct {
|
// Fault type
|
||||||
|
type Fault struct {
|
||||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
|
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
|
||||||
|
|
||||||
Code string `xml:"faultcode,omitempty"`
|
Code string `xml:"faultcode,omitempty"`
|
||||||
@ -30,7 +35,8 @@ type SOAPFault struct {
|
|||||||
Detail string `xml:"detail,omitempty"`
|
Detail string `xml:"detail,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SOAPBody) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
// UnmarshalXML implement xml.Unmarshaler
|
||||||
|
func (b *Body) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
if b.Content == nil {
|
if b.Content == nil {
|
||||||
return xml.UnmarshalError("Content must be a pointer to a struct")
|
return xml.UnmarshalError("Content must be a pointer to a struct")
|
||||||
}
|
}
|
||||||
@ -56,7 +62,7 @@ Loop:
|
|||||||
if consumed {
|
if consumed {
|
||||||
return xml.UnmarshalError("Found multiple elements inside SOAP body; not wrapped-document/literal WS-I compliant")
|
return xml.UnmarshalError("Found multiple elements inside SOAP body; not wrapped-document/literal WS-I compliant")
|
||||||
} else if se.Name.Space == "http://schemas.xmlsoap.org/soap/envelope/" && se.Name.Local == "Fault" {
|
} else if se.Name.Space == "http://schemas.xmlsoap.org/soap/envelope/" && se.Name.Local == "Fault" {
|
||||||
b.Fault = &SOAPFault{}
|
b.Fault = &Fault{}
|
||||||
b.Content = nil
|
b.Content = nil
|
||||||
|
|
||||||
err = d.DecodeElement(b.Fault, &se)
|
err = d.DecodeElement(b.Fault, &se)
|
||||||
@ -66,6 +72,7 @@ Loop:
|
|||||||
|
|
||||||
consumed = true
|
consumed = true
|
||||||
} else {
|
} else {
|
||||||
|
b.SOAPBodyContentType = se.Name.Local
|
||||||
if err = d.DecodeElement(b.Content, &se); err != nil {
|
if err = d.DecodeElement(b.Content, &se); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -80,6 +87,6 @@ Loop:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *SOAPFault) Error() string {
|
func (f *Fault) Error() string {
|
||||||
return f.String
|
return f.String
|
||||||
}
|
}
|
||||||
|
|||||||
42
test/test.go
42
test/test.go
@ -1,42 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/foomo/soap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FooRequest struct {
|
|
||||||
Foo string
|
|
||||||
}
|
|
||||||
|
|
||||||
type FooResponse struct {
|
|
||||||
Bar string
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunServer() {
|
|
||||||
soapServer := soap.NewServer()
|
|
||||||
/*
|
|
||||||
soapServer.HandleOperation(
|
|
||||||
"operationFoo",
|
|
||||||
"FooRequest",
|
|
||||||
func() interface{} {
|
|
||||||
return &FooRequest{}
|
|
||||||
},
|
|
||||||
func(request interface{}) (response interface{}, err error) {
|
|
||||||
fooRequest := request.(*FooRequest)
|
|
||||||
fooResponse := &FooResponse{
|
|
||||||
Bar: "Hello " + fooRequest.Foo,
|
|
||||||
}
|
|
||||||
response = fooResponse
|
|
||||||
return
|
|
||||||
},
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
err := soapServer.ListenAndServe(":8080")
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
RunServer()
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user