25

I create my form like this:

form := url.Values{}
form.Add("region", "San Francisco")
if len(params) > 0 { 
    for i := 0; i < len(params); i += 2 { 
        form.Add(params[i], params[i+1])
    }
    testLog.Infof("form %v", form)

Now if I use

resp, err = http.PostForm(address+r.Path, form)

then everything works fine, I get back a response with an expected cookie.

However, I would like to to add a header in which case I can't use PostForm hence I created my POST request manually like:

req, err := http.NewRequest("POST", address+r.Path, strings.NewReader(form.Encode()))

Then I add stuff to the header and send the request

req.Header.Add("region", "San Francisco") 
resp, err = http.DefaultClient.Do(req)

But the form is not received and my response does not contain any cookie.

When I print the req, it looks like the form is nil:

&{POST http://localhost:8081/login HTTP/1.1 1 1 map[Region:[San Francisco]] {0xc420553600} 78 [] false localhost:8081 map[] map[] <nil> map[]   <nil> <nil> <nil> <nil>}
Jana
  • 5,516
  • 5
  • 23
  • 29
  • This is a webpage posting the form, right? You're gonna have to use JS for this if that is the case. When you're working with the template, Go code isn't posting anything, it's simply generating the html with an input element of type submit. That request on the other hand can't run in the browser, it's gotta run on the system. So if you have an html form using submit to POST to a Go back end and you want to add headers you need to use an `XMLHttpRequest` in JavaScript to accomplish that. – evanmcdonnal Oct 12 '16 at 19:25
  • no, not a webpage, this is a Go unit test. See the answer below, setting the content-type in header fixed my problem. – Jana Oct 12 '16 at 21:18

1 Answers1

54

You need to add a content type to your request.

You said http.PostForm worked so let's look at the source of that:

func PostForm(url string, data url.Values) (resp *Response, err error) {
    return DefaultClient.PostForm(url, data)
}

OK so it's just a wrapper around the PostForm method on the default client. Let's look at that:

func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
    return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

OK it's calling the Post method and passing in "application/x-www-form-urlencoded" for bodyType and doing the same thing for the body that you're doing. Let's look at the Post method

func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
    req, err := NewRequest("POST", url, body)
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", bodyType)
    return c.doFollowingRedirects(req, shouldRedirectPost)
}

So the solution to your problem is to add

req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
Franck Jeannin
  • 6,107
  • 3
  • 21
  • 33
jcbwlkr
  • 7,789
  • 2
  • 22
  • 28