How to do a SOAP call in Go? [closed] - soap

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Given that Adwords is a Google thing, and Go is a Google thing, how long until there's a version of the Adwords API written in Go?
Associated with that question, another: are there any SOAP libraries for Go yet?

I can' answer about the adwords API as I saw no announcement from the company and predicting the time before a release can hardly be done from outside.
So I'll answer your second question.
I don't know any SOAP library in Go (go-lang.cat-v.org doesn't seem to reference one) but as in most languages, a way to deal with simple SOAP messages is to use the basic http and xml libraries.
The two important operations are
1) to get an answer by doing a POST query :
resp, err := httpClient.Post(query, "text/xml; charset=utf-8", someXMLasBytes)
2) to decode it using a xml.NewDecoder into the desired structure :
parser := xml.NewDecoder(bytes.NewBufferString(in))
err = parser.DecodeElement(&envelope, nil)
Here's a complete and commented example of a SOAP query done in Go (simplified from this) :
package main
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
)
// The URL of the SOAP server
const MH_SOAP_URL = "http://sp.mountyhall.com/SP_WebService.php"
// this is just the message I'll send for interrogation, with placeholders
// for my parameters
const SOAP_VUE_QUERY_FORMAT = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tns=\"urn:SP_WebService\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" ><SOAP-ENV:Body><mns:Vue xmlns:mns=\"uri:mhSp\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><numero xsi:type=\"xsd:string\">%d</numero><mdp xsi:type=\"xsd:string\">%s</mdp></mns:Vue></SOAP-ENV:Body></SOAP-ENV:Envelope>"
// Here I define Go structures, almost identical to the structure of the
// XML message we'll fetch
// Note that annotations (the string "return>item") allow to have a slightly
// different structure or different namings
type SoapItem struct {
Numero int
Nom string
Type string
PositionX int
PositionY int
PositionN int
Monde int
}
type SoapVue struct {
Items []SoapItem "return>item"
}
type SoapFault struct {
Faultstring string
Detail string
}
type SoapBody struct {
Fault SoapFault
ProfilResponse SoapProfil
VueResponse SoapVue
}
type SoapEnvelope struct {
XMLName xml.Name
Body SoapBody
}
// Here is the function querying the SOAP server
// It returns the whole answer as a Go structure (a SoapEnvelope)
// You could also return an error in a second returned parameter
func GetSoapEnvelope(query string, numero int, mdp string) (envelope *SoapEnvelope) {
soapRequestContent := fmt.Sprintf(query, numero, mdp)
httpClient := new(http.Client)
resp, err := httpClient.Post(MH_SOAP_URL, "text/xml; charset=utf-8", bytes.NewBufferString(soapRequestContent))
if err != nil {
// handle error
}
b, e := ioutil.ReadAll(resp.Body) // probably not efficient, done because the stream isn't always a pure XML stream and I have to fix things (not shown here)
if e != nil {
// handle error
}
in := string(b)
parser := xml.NewDecoder(bytes.NewBufferString(in))
envelope = new(SoapEnvelope) // this allocates the structure in which we'll decode the XML
err = parser.DecodeElement(&envelope, nil)
if err != nil {
// handle error
}
resp.Body.Close()
return
}

The Google APIs for Go is a work in progress.

Related

Request created with http.NewRequestWithContext() looses context when passed to middleware

In program bellow I have two routers. One is working at localhost:3000 and acts like a public access point. It also may send requests with data to another local address which is localhost:8000 where data is being processed. Second router is working at localhost:8000 and handles processing requests for the first router.
Problem
The first router sends a request with context to the second using http.NewRequestWithContext() function. The value is being added to the context and the context is added to request. When request arrives to the second router it does not have value that was added previously.
Some things like error handling are not being written to not post a wall of code here.
package main
import (
"bytes"
"context"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
)
func main() {
go func() {
err := http.ListenAndServe(
"localhost:3000",
GetDataAndSolve(),
)
if err != nil {
panic(err)
}
}()
go func() {
err := http.ListenAndServe( // in GetDataAndSolve() we send requests
"localhost:8000", // with data for processing
InternalService(),
)
if err != nil {
panic(err)
}
}()
// interrupt := make(chan os.Signal, 1)
// signal.Notify(interrupt, syscall.SIGTERM, syscall.SIGINT)
// <-interrupt // just a cool way to close the program, uncomment if you need it
}
func GetDataAndSolve() http.Handler {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/tasks/str", func(rw http.ResponseWriter, r *http.Request) {
// receiving data for processing...
taskCtx := context.WithValue(r.Context(), "str", "strVar") // the value is being
postReq, err := http.NewRequestWithContext( // stored to context
taskCtx, // context is being given to request
"POST",
"http://localhost:8000/tasks/solution",
bytes.NewBuffer([]byte("something")),
)
postReq.Header.Set("Content-Type", "application/json") // specifying for endpoint
if err != nil { // what we are sending
return
}
resp, err := http.DefaultClient.Do(postReq) // running actual request
// pls, proceed to Solver()
// do stuff to resp
// also despite arriving to middleware without right context
// here resp contains a request with correct context
})
return r
}
func Solver(next http.Handler) http.Handler { // here we end up after sending postReq
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if r.Context().Value("str").(string) == "" {
return // the request arrive without "str" in its context
}
ctxWithResult := context.WithValue(r.Context(), "result", mockFunc(r.Context()))
next.ServeHTTP(rw, r.Clone(ctxWithResult))
})
}
func InternalService() http.Handler {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.With(Solver).Post("/tasks/solution", emptyHandlerFunc)
return r
}
Your understanding of context is not correct.
Context (simplifying to an extent and in reference to NewRequestWithContext API), is just an in-memory object using which you can control the lifetime of the request (Handling/Triggering cancellations).
However your code is making a HTTP call, which goes over the wire (marshaled) using HTTP protocol. This protocol doesn't understand golang's context or its values.
In your scenario, both /tasks/str and /tasks/solution are being run on the same server. What if they were on different servers, probably different languages and application servers as well, So the context cannot be sent across.
Since the APIs are within the same server, maybe you can avoid making a full blown HTTP call and resort to directly invoking the API/Method. It might turn out to be faster as well.
If you still want to send additional values from context, then you'll have to make use of other attributes like HTTP Headers, Params, Body to send across the required information. This can provide more info on how to serialize data from context over HTTP.

How to persist a file (much less than 16MB) in MongoDB using official go-driver

I'm trying to persist some files in MongoDB. Not interested in GridFS as files are always less than 256KB. It seems difficult to find any good example on how this typically is done.
What I do is open temporary file for reading to get an io.Reader and use bson.NewFromIOReader(io.Reader) to read bytes into bson.Raw type. However I always get an exception unexpected EOF. After some investigation it seems that this function uses io.ReadFull which may throw that exception. So I'm wondering what I do wrong. I can perform io.Copy operation on the same reader without such exception.
Any ideas?
If your files are only a few KBs, simplest, cleanest (and maybe even fastest) is to use ioutil.ReadFile() to read the complete file into a byte slice. Save the document where one of the property's value is this byte slice. No magic.
For example:
data, err := ioutil.ReadFile("filename.txt")
if err != nil {
// Handle error
}
doc := bson.M{
"data": data,
"created": time.Now(),
}
coll := ... // Obtain collection
if _, err := coll.InsertOne(context.Background(), doc); err != nil {
// handle error
}
In your actual example you may want to use a struct to model your document, e.g.:
type Doc struct {
ID primitive.ObjectID `bson:"_id"`
Data []byte `bson:"data"`
Created time.Time `bson:"created"`
}
And then use:
doc := &Doc{
Data: data,
Created: time.Now(),
}

Proper way to query to check if credentials already exist [duplicate]

This question already has answers here:
Scan function by reference or by value
(1 answer)
I want to check if record exist and if not exist then i want to insert that record to database using golang
(4 answers)
Closed 2 years ago.
I currently have:
func foo (w http.ResponseWriter, req *http.Request) {
chekr := `SELECT FROM public."Users" WHERE email=$1`
err = db.QueryRow(chekr, usr.Email).Scan()
if err != sql.ErrNoRows {
data, err := json.Marshal("There is already a user with this email")
if err != nil { w.Write(data) }
}
// code that should run if email isn't found
}
However, I find it never working and always passing the if block.
As the above comment stated, I forgot the */1. QueryRow works, I just had another error somewhere. As others have stated there's others errors, this is just for one case to test.

How to compare UINT (not uint64 or uint32) with string [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have written following code and want to check if typed URL in REST API is same with user_id in header to grant the access or return a failure message, but there is following error:
"invalid operation: userId != vars["user_id"] (mismatched types uint and string)"
func UserIdAutentication(w http.ResponseWriter,r *http.Request) {
id := r.Context().Value("user").(uint)
vars:=mux.Vars(r)
if userId != vars["user_id"] {
response := u.Message(false, "User not Autorized to fetch other users data")
w.WriteHeader(http.StatusForbidden)
w.Header().Add("Content-Type", "application/json")
u.Respond(w, response)
return
} else {
return
}
}
You should convert user_id to uint before compare to another unit.
id := r.Context().Value("user").(uint)
vars:=mux.Vars(r)
userID, err := strconv.ParseUint(vars["user_id"], 0, 64)
if err != nil {
// Do something with eror
}
if userId!=uint(userID){
response := u.Message(false, "User not Autorized to fetch other users data")
w.WriteHeader(http.StatusForbidden)
w.Header().Add("Content-Type", "application/json")
u.Respond(w, response)
return
}else {
return
}

Sending email onward that was parsed using Golang net/mail.ReadMessage

I'm looking for the cleanest way in Golang to transfer a message through (i.e. act as an SMTP proxy) while performing some manipulation on the message body html (e.g. adding an open tracking pixel - not yet coded).
The net/mail package includes a method ReadMessage that parses mail headers into a map, and gives you an io.Reader for the body. This is necessary to determine the MIME parts of the body for processing, rather than just io.Copying them through. (the simple stub version of this function, shown in the block comment, does just that).
The following function copies an incoming mail "src" to an outgoing mail stream "dest". (The calling code sets these up as DotReader and DotWriter which takes care of most of the "dot" processing needed for RFC5321.
// Processing of email body via IO stream functions
package main
import (
"bufio"
"io"
"log"
"net/mail"
"strings"
)
/* If you just want to pass through the entire mail headers and body, you can just use
the following alernative:
func MailCopy(dst io.Writer, src io.Reader) (int64, error) {
return io.Copy(dst, src)
}
*/
// MailCopy transfers the mail body from downstream (client) to upstream (server)
// The writer will be closed by the parent function, no need to close it here.
func MailCopy(dst io.Writer, src io.Reader) (int64, error) {
var totalWritten int64
const smtpCRLF = "\r\n"
message, err := mail.ReadMessage(bufio.NewReader(src))
if err != nil {
return totalWritten, err
}
// Pass through headers. The m.Header map does not preserve order, but that should not matter.
for hdrType, hdrList := range message.Header {
for _, hdrVal := range hdrList {
hdrLine := hdrType + ": " + hdrVal + smtpCRLF
log.Print("\t", hdrLine)
bytesWritten, err := dst.Write([]byte(hdrLine))
totalWritten += int64(bytesWritten)
if err != nil {
return totalWritten, err
}
}
}
// Blank line denotes end of headers
bytesWritten, err := io.Copy(dst, strings.NewReader(smtpCRLF))
totalWritten += int64(bytesWritten)
if err != nil {
return totalWritten, err
}
// Copy the body
bytesWritten, err = io.Copy(dst, message.Body)
totalWritten += int64(bytesWritten)
if err != nil {
return totalWritten, err
}
return totalWritten, err
}
It does seem necessary to build this, because there is no net/mail.WriteMessage() method.
the header order is always randomised by Golang's map functionality. This seems harmless in my tests
A forced CRLF needs to be put in between the end of the headers and the body, as per RFCs. DotWriter takes care of the terminating dot.
The function shown above works, I was wondering if there is a better way to do this?