1

I want to call FormValue on golang net/http Request from several middleware handler functions before serving the request. And I do not want to invalidate the request while doing this.

It works fine except when the incoming request has a multipart form-data, the data gets invalidated after I call FormValue and there is nothing to parse in the final route.

3
  • 1
    Can you be more specific than "data gets invalidated"? What error are you seeing? Subsequent calls return "" or some sort of panic... or? Commented Nov 16, 2016 at 23:55
  • 1
    From the docs: "FormValue calls ParseMultipartForm and ParseForm if necessary"... and "After one call to ParseMultipartForm, subsequent calls have no effect.". FormValue() may be intrinsically non-repeatable (which would sound like a bug to me!). Is there a simple example to test? Commented Nov 16, 2016 at 23:57
  • the content is nil. Commented Nov 17, 2016 at 0:47

1 Answer 1

5

I wrote a utility function that solved my problem:

package utils

import (
    "bytes"
    "io"
    "io/ioutil"
    "mime"
    "mime/multipart"
    "net/http"
    "strings"
)

// Get form values without invalidating the request body in case the data is multiform
func GetFormValues(request *http.Request, keys []string) []string {
    var values []string
    mediaType, params, err := mime.ParseMediaType(request.Header.Get("Content-Type"))
    if err != nil || !strings.HasPrefix(mediaType, "multipart/") {
        for i := range keys {
            values = append(values, request.FormValue(keys[i]))
        }
    } else { // multi form
        buf, _ := ioutil.ReadAll(request.Body)
        origBody := ioutil.NopCloser(bytes.NewBuffer(buf))
        var rdr = multipart.NewReader(bytes.NewBuffer(buf), params["boundary"])
        for len(values) < len(keys) {
            part, err_part := rdr.NextPart()
            if err_part == io.EOF {
                break
            }
            for i := range keys {
                if part.FormName() == keys[i] {
                    buf := new(bytes.Buffer)
                    buf.ReadFrom(part)
                    values = append(values, buf.String())
                }
            }
        }
        request.Body = origBody
    }
    if len(values) == len(keys) {
        return values
    } else {
        return nil
    }
}

// Get form value without invalidating the request body in case the data is multiform
func GetFormValue(request *http.Request, key string) string {
    if result := GetFormValues(request, []string{key}); len(result) == 1 {
        return result[0]
    } else {
        return ""
    }
}

Now instead of calling

value := request.FormValue(key)

I do

value := utils.GetFormValue(request, key)

or for multiple values

values := utils.GetFormValues(request, []string{keys...})
Sign up to request clarification or add additional context in comments.

2 Comments

Cool! Should this maybe be a bug / rfe on github.com/golang/go/issues? (It looks like the current behavior was the result of fixing a bug that was causing a /segfault/ in this use case!)
I agree this should be a bug, I think it is very intuitive to expect the request body to be nil after calling FormValue function.

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.