Welcome To Golang By Example

HTTP server or Parse incoming application/x-www-form-urlencoded request body in Go (Golang)

Note: This post is parsing the application/x-www-form-urlencoded request at the server end. If you using an HTTP client and trying to send the application/x-www-form-urlencoded request then please see the below link

https://golangbyexample.com/http-client-urlencoded-body-go/

Table of Contents

Overview

The applcation/x-www-form-urlencoded content-type request body is like a giant query string. Similar to the query string in a URI it is a key-value pair having the below format

key1=value1&key2=value21&key2=value22&key3=value3

where there are below key-value pairs

Each key-value pair is separated by & in case of multiple values for the same key there will be two entries of that key-value pair. Also, each key and value is URLencoded similar to the query string.

Now the question that might be coming to the mind is if x-www-form-urlencoded is just like query string then why does it exist. The reason is that the query string is part of the URI and since there is a limit on the length of the URI, you can send a limited number of key-value pairs in the query string.  While there is no limit for the length of the x-www-form-urlencoded request body. However, it is limited by the max request body size allowed by the server which is generally 10MB for most of the servers. Now let’s see how we can parse the x-www-form-urlencoded in golang

Example

Request object of net/http package has two fields which can hold the form-data. https://golang.org/src/net/http/request.go

The two fields are

To get the x-www-form-urlencoded request body we need to first call the below function on the request object

request.ParseForm()

This function essentially does below things

request.Form
request.Form
request.PostForm

Both Form and PostForm field are of the below type

type Values map[string][]string

It is a map where

So essentially to get the x-www-url-encoded request body we need to call the ParseForm function first on the request object. This will populate both the Form and PostForm field. Then we can access these fields to get the x-www-form-urlencoded body. For example, let’s say we have the below x-www-form-urlencoded body.

name=John
age=21
hobbies=sports
hobbies=music

Then you can access the name field like below

request.Form["name"]
request.PostForm["name"]

both will return

["John"]

It is an array because the key can have multiple values.

Also, the request’s object defines one more function FormValue which can be used to get the first value associated with the key. It just returns the first value and not the array. For example

request.FormValue("hobbies")

will return

sports

Let’s see a program for that as well

package main

import (
	"fmt"
	"net/http"
)

type employee struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	createEmployeeHanlder := http.HandlerFunc(createEmployee)
	http.Handle("/employee", createEmployeeHanlder)
	http.ListenAndServe(":8080", nil)
}

func createEmployee(w http.ResponseWriter, r *http.Request) {
	headerContentTtype := r.Header.Get("Content-Type")
	if headerContentTtype != "application/x-www-form-urlencoded" {
		w.WriteHeader(http.StatusUnsupportedMediaType)
		return
	}
	r.ParseForm()
	fmt.Println("request.Form::")
	for key, value := range r.Form {
		fmt.Printf("Key:%s, Value:%s\n", key, value)
	}
	fmt.Println("\nrequest.PostForm::")
	for key, value := range r.PostForm {
		fmt.Printf("Key:%s, Value:%s\n", key, value)
	}

	fmt.Printf("\nName field in Form:%s\n", r.Form["name"])
	fmt.Printf("\nName field in PostForm:%s\n", r.PostForm["name"])
	fmt.Printf("\nHobbies field in FormValue:%s\n", r.FormValue("hobbies"))

	w.WriteHeader(200)
	return
}

In the above program, we are running a server on port 8080. Also notice that we are first calling the ParseForm function and then accessing the Form and PostForm fields of the request object.

Also, see how we are accessing the value of the name field in Form as well as PostForm

r.Form["name"]
r.PostForm["name"]

Also, we are calling the FormValue function on the request object to access the hobbies field as below

r.FormValue("hobbies")

Run the above program. It will start a server at port 8080

Now let’s try some curl calls.

curl -v -X POST 'http://localhost:8080/employee' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'name=John' \
--data-urlencode 'age=18' \
--data-urlencode 'hobbies=sports' \
--data-urlencode 'hobbies=music'

Output

request.Form:: 
Key:name, Value:[John] 
Key:age, Value:[18] 
Key:hobbies, Value:[sports music] 

request.PostForm:: 
Key:name, Value:[John] 
Key:age, Value:[18] 
Key:hobbies, Value:[sports music] 

Name field in Form:[John] 

Name field in PostForm:[John] 

Hobbies field in FormValue:sports
curl -v -X POST 'http://localhost:8080/employee?gender=male' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'name=John' \
--data-urlencode 'age=18' \
--data-urlencode 'hobbies=sports' \
--data-urlencode 'hobbies=music'

Output

request.Form:: 
Key:age, Value:[18] 
Key:hobbies, Value:[sports music] 
Key:gender, Value:[male] 
Key:name, Value:[John] 

request.PostForm:: 
Key:name, Value:[John] 
Key:age, Value:[18] 
Key:hobbies, Value:[sports music] 

Name field in Form:[John] 

Name field in PostForm:[John] 

Hobbies field in FormValue:sports
curl -v -X POST 'http://localhost:8080/employee?age=20' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'name=John' \
--data-urlencode 'age=18' \
--data-urlencode 'hobbies=sports' \
--data-urlencode 'hobbies=music'

Output

request.Form:: 
Key:hobbies, Value:[sports music] 
Key:name, Value:[John] 
Key:age, Value:[18 20] 

request.PostForm:: 
Key:name, Value:[John] 
Key:age, Value:[18] 
Key:hobbies, Value:[sports music] 

Name field in Form:[John] 

Name field in PostForm:[John] 

Hobbies field in FormValue:sports