1

i'm trying to fetch some data from a HTML Webpage via Javascript to my Golang Webserver. But the Data does'nt seem to be received there.

This is my Javascript Function, which is called, when selecting something in my dropdown menu of my webpage:

async function onDataFormatSelect(dataFormat) 
    {
      console.log('Change DataFormat', dataFormat);
      let formDataSelect = new FormData();
      formDataSelect.append("cmd", "change");
      const fetchoptions  = {
        method: 'POST',
        body: formDataSelect,
      };

      let r = await fetch('/myURL', fetchoptions);
      console.log('HTTP response code.', r.status);
      for (var pair of formDataSelect.entries()) {
        console.log(pair[0]+ ', ' + pair[1]); 
      }
    }

In SetupRoutes() in my Go Webserver, the URL gets an Handler:

    http.HandleFunc("/myURL", urlHandler)

This is my Handler:

func urlHandler(w http.ResponseWriter, r *http.Request) {
    

    if r.Method == "POST" {
        r.ParseForm()

        topicName := r.FormValue("topicName")
        cmd := r.FormValue("cmd")
        log.Println("action: " + cmd)
        switch cmd {
        ...
        case "change": //Change DataFormat of Topic
            log.Println("cmd is " + cmd)
            newDataFormat := r.FormValue("dataFormat")
            //Call another function
        default:
            log.Println("unknown command: " + cmd)
        }

    } else {
        log.Println("using wrong method (GET)")
    }

    http.Redirect(w, r, appdata.Status.ProxyRedirect, http.StatusMovedPermanently)
}

The Output of Javascript is correct. The FormDataSelect is filled correctly, but in Go the "cmd" is not there. Somehow in another Function with the same setup everything is working correctly.

Can you help me or do you have any Workaround for me beside using XMLHttpRequest?

4
  • potentially you need to set the proper content type on the client side as header. Commented May 25, 2021 at 9:31
  • @TheFool — No. fetch will set the Content-Type header automatically based on the content. For FormData objects it will set it to be multipart and will get the mandatory boundary parameter which we can't predict from the code. Commented May 25, 2021 at 9:35
  • Do you have a source to that? Automatically setting content type? Commented May 25, 2021 at 9:41
  • fetch.spec.whatwg.org/#bodyinit-unions Commented May 25, 2021 at 9:46

2 Answers 2

2

FormData objects are designed to support posting files so they always send multipart requests.

See the documentation for Go's http module.

You're using parseForm which says:

when the Content-Type is not application/x-www-form-urlencoded, the request Body is not read, and r.PostForm is initialized to a non-nil, empty value

You need to use ParseMultipartForm instead.

Sign up to request clarification or add additional context in comments.

2 Comments

Which maxMemory parameter do i have to give ParseMultipartForm? I just gave it 200 and it worked. But thats not very beautiful.
Depends on how big your requests are and how much memory your server has. Since you only seem to be sending a string, "not much". 200 bytes is probably small even for that. 1000000 is one megabyte, which should be plenty for any request not actually uploading files.
0

I've used Perl, PHP, Python, and Nodejs professionally for a long time, and handing GET and POST is pretty universal. In these languages POST can be a FORM DATA or FETCH JSON because they both are in the REQUEST BODY. Typically the languages automatically parse these regardless.

FORM DATA

`name=johndoe&age=100`

FETCH JSON

`{"name": "john doe", "age": 100}`

However, golang doesn't have anything built-in to automatically parse FETCH JSON. That being said... it is still possible and here is my example.

Javascript

fetch('website.com/api/login', {
    method: "POST",
    body: JSON.stringify("name: "john doe")
})
.then((resp)=>{
    return resp.json()
})
.then((data)=>{
    console.log(data)
})

Golang

import (
    "net/http"
    "io/ioutil"
    "encoding/json" 
)

http.HandleFunc("/api/login", func(w http.ResponseWriter, r *http.Request){
    defer r.Body.Close()

    // parse body to a string
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        fmt.Print("error")
    }
    fmt.Println(string(body))

    // parse body string as json
    type JsonString struct {
        Name string
        Age int
    }
    var jsonData JsonString
    err2 := json.Unmarshal(body, &jsonData)
    if err2 != nil {
        fmt.Println("error")
    }

    fmt.Println(jsonData.name, string(jsonData.age))

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "thanks for the data"}`))
})
http.ListenAndServe(":8080", nil)

That should work for you, but if you also need to look for FORM DATA that is a completely different process.

Golang

Golang

import (
    "net/http"
    "io/ioutil"
    "encoding/json" 
)

http.HandleFunc("/api/login", func(w http.ResponseWriter, r *http.Request){
    r.ParseForm()
    name := r.PostForm.get("name")
    age := r.PostForm.get("age")
    fmt.Println(name, age)

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"message": "thanks for the data"}`))
})
http.ListenAndServe(":8080", nil)

It is sort of crazy how much easier this is... As I mentioned above, there might be something I couldn't find. I've ran into a few things that Golang just have really weird names for compared to every other language and I just didn't know.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.