Skip to main content

Golang Refresher :: url & http

Guangzhou, China

The Go Standard Library

url|http|encoding-json|encoding-xml :: Networking and data processing

Package url parses URLs and implements query escaping.

Working with URLs

package main

import (
"fmt"
"net/url"
)

func main() {

// Define a url
u := "https://mpolinowski.github.io:80/?chapter_filter=%22Dev+Notes%22&type_filter=%22Note%22&q=%22Golang%22&tag_filter=%5B%22Golang%22%5D"

// Parse url into it's parts
result, _ := url.Parse(u)
fmt.Println(result.Scheme)
fmt.Println(result.Host)
fmt.Println(result.Path)
fmt.Println(result.Port())
fmt.Println(result.RawQuery)

// Extract query's into variables
val := result.Query()
fmt.Println("Search:", val["q"])
fmt.Println("Tags:", val["tag_filter"])
fmt.Println("Types:", val["type_filter"])
fmt.Println("Chapters:", val["chapter_filter"])

// Create URL from components
newURL := &url.URL {
Scheme: "https",
Host: "mpolinowski.github.io",
Path: "/devnotes",
RawQuery: "usr=admin&pwd=password",
}

// Print created url
s := newURL.String()
fmt.Println(s)
// Modify url
newURL.RawQuery = "usr=user&pwd=1234"
s = newURL.String()
fmt.Println(s)

// Generate new url queries
newvals := url.Values{}
newvals.Add("pwd", "nopwd")
newvals.Add("usr", "visitor")

newURL.RawQuery = newvals.Encode()
s = newURL.String()
fmt.Println(s)

}
go run .\main.go
https
mpolinowski.github.io:80
/
80
chapter_filter=%22Dev+Notes%22&type_filter=%22Note%22&q=%22Golang%22&tag_filter=%5B%22Golang%22%5D
Search: ["Golang"]
Tags: [["Golang"]]
Types: ["Note"]
Chapters: ["Dev Notes"]
https://mpolinowski.github.io/devnotes?usr=admin&pwd=password
https://mpolinowski.github.io/devnotes?usr=user&pwd=1234
https://mpolinowski.github.io/devnotes?pwd=nopwd&usr=visitor

HTTP GET

Package http provides HTTP client and server implementations. Get, Head, Post, and PostForm make HTTP (or HTTPS) requests.

Example: Read the INSTAR IP camera state through it's HTTP REST API.

package main

import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)

func main() {

// Define API endpoint - get the PIR motion sensor state:
const cameraAPI = "http://192.168.2.77:8090/param.cgi?cmd=getpirattr&usr=admin&pwd=instar"

// Make GET request
resp, err := http.Get(cameraAPI)
if err != nil {
return
}

// Closing response
defer resp.Body.Close()

// Splitting up the response
fmt.Println("Status:", resp.Status)
fmt.Println("Status Code:", resp.StatusCode)
fmt.Println("Protocol:", resp.Proto)
fmt.Println("Content Length:", resp.ContentLength)

// Build content from received bytes
var sb strings.Builder
content, _ := ioutil.ReadAll(resp.Body)
bytecount, _ := sb.Write(content)
// Format the output
fmt.Println(bytecount, sb.String())


}
go run .\main.go
Status: 200 OK
Status Code: 200
Protocol: HTTP/1.1
Content Length: 40
40 var pir_enable="1";
var pir_flag="0";

HTTP POST with Basic Auth

Example: Send a post request with basic authentication to switch on your INSTAR IP cameras PIR sensor.

package main

import (
"fmt"
"net/http"
"time"
)

var (
// Camera admin login
username = "admin"
password = "instar"
// API endpoint to activate the PIR sensor
cameraAPI = "http://192.168.2.77:8090/param.cgi?cmd=setpirattr&-pir_enable=1"
)

func main() {
postCommand(cameraAPI, "POST")
}

func postCommand(url, method string) error {
// Send request and catch error
client := &http.Client{
Timeout: time.Second * 10,
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
return fmt.Errorf("Got error %s", err.Error())
}
// Authenticate and catch error
req.SetBasicAuth(username, password)
response, err := client.Do(req)
if err != nil {
return fmt.Errorf("Got error %s", err.Error())
}
defer response.Body.Close()
return nil
}

HTTP POST with JSON Request Body

package main

import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)

func main() {

// Define API endpoint - activate the PIR sensor
const httpbin = "https://httpbin.org/post"

// Make a POST request
// creat the JSON request body:
reqBody := strings.NewReader(`
{
"val": "this is a value",
"num": 42
}
`)

// post the generated request body to the camera API
resp, err := http.Post(httpbin, "application/json", reqBody)
if err != nil {
return
}

// Read the response
content, _ := ioutil.ReadAll(resp.Body)
// Format the output
fmt.Printf("%s\n", content)


// Closing response
defer resp.Body.Close()

// Post form data
data := url.Values{}
data.Add("formField1", "Content of form field 1")
data.Add("formField2", "Content of form field 2")
respForm, err := http.PostForm(httpbin, data)
contentForm, _ := ioutil.ReadAll(respForm.Body)
defer respForm.Body.Close()
fmt.Printf("%s\n", contentForm)
}
go run .\main.go
{
"args": {},
"data": "\n\t\t{\n\t\t\t\"val\": \"this is a value\",\n\t\t\t\"num\": 42\n\t\t}\n\t",
"files": {},
"form": {},
"headers": {
"Accept-Encoding": "gzip",
"Content-Length": "52",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "Go-http-client/2.0",
"X-Amzn-Trace-Id": "Root=1-615bcc9f-17ca354f4b8b8ec705a75dcf"
},
"json": {
"num": 42,
"val": "this is a value"
},
"origin": "103.125.234.111",
"url": "https://httpbin.org/post"
}

{
"args": {},
"data": "",
"files": {},
"form": {
"formField1": "Content of form field 1",
"formField2": "Content of form field 2"
},
"headers": {
"Accept-Encoding": "gzip",
"Content-Length": "69",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Go-http-client/2.0",
"X-Amzn-Trace-Id": "Root=1-615bcc9f-71f982c9376e6bef4abb92b7"
},
"json": null,
"origin": "103.125.234.111",
"url": "https://httpbin.org/post"
}

Encode Go Sructs to JSON

package main

import (
"encoding/json"
"fmt"
)

// Set types and assign the keys that should be used e.g. Characters -> char
// Omit empty fields and don't use actor names
type person struct {
Character string `json:"char"`
Actor string `json:"-"`
PlaceOfBirth string `json:"loc"`
Seasons []int `json:"s,omitempty"`
}

func encodeJSON() {
// Create JSON data
people := []person {
{"Joe Miller", "Thomas Jane", "Ceres", []int{1,2,3,4} },
{"James Holden", "Steven Strait", "Earth", []int{1,2,3,4,5,6} },
{"Bobbie Draper", "Frankie Adams", "Mars", []int{2,3,4,5,6} },
{"Camina Drummer", "Cara Gee", "Tycho Station", []int{4,5,6} },
{"Jean-Luc Picard", "Patrick Stewart", "Earth", nil },
}

// Use Marshal to convert data structure to JSON
result, err := json.Marshal(people)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", result)

// Use MarshalIndent to convert data structure to formatted JSON
resultFormatted, err := json.MarshalIndent(people, "", "\t")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", resultFormatted)

}

func main() {

encodeJSON()


}
go run .\main.go
[{"char":"Joe Miller","loc":"Ceres","s":[1,2,3,4]},{"char":"James Holden","loc":"Earth","s":[1,2,3,4,5,6]},{"char":"Bobbie Draper","loc":"Mars","s":[2,3,4,5,6]},{"char":"Camina Drummer","loc":"Tycho Station","s":[4,5,6]},{"char":"Jean Luc Picard","loc":"Earth"}]
[
{
"char": "Joe Miller",
"loc": "Ceres",
"s": [
1,
2,
3,
4
]
},
{
"char": "James Holden",
"loc": "Earth",
"s": [
1,
2,
3,
4,
5,
6
]
},
{
"char": "Bobbie Draper",
"loc": "Mars",
"s": [
2,
3,
4,
5,
6
]
},
{
"char": "Camina Drummer",
"loc": "Tycho Station",
"s": [
4,
5,
6
]
},
{
"char": "Jean Luc Picard",
"loc": "Earth"
}
]

Decode JSON

package main

import (
"encoding/json"
"fmt"
)

// Set types and assign the keys that should be used e.g. Characters -> char
// Omit empty fields and don't use actor names
type person struct {
Character string `json:"char"`
Actor string `json:"-"`
PlaceOfBirth string `json:"loc"`
Seasons []int `json:"s,omitempty"`
}

func decodeJSON() {
// Declare JSON data to decode
data := []byte(`
{
"char": "Joe Miller",
"actor": "Thomas Jane",
"loc": "Ceres",
"s": [1,2,3,4]
}
`)

// Declare person struct of type person for the JSON data
var p person
// Check if JSON is valid and un-marshal
valid := json.Valid(data)
if valid {
json.Unmarshal(data, &p)
fmt.Printf("%#v\n", p)
}

}

func mapJSON() {

// Declare JSON data to decode
dataMap := []byte(`
{
"char": "Camina Drummer",
"actor": "Cara Gee",
"loc": "Tycho Station",
"s": [4,5,6]
}
`)
// Decode JSON into a map structure
var m map[string]interface{}
json.Unmarshal(dataMap, &m)
fmt.Printf("%#v\n", m)

// Iterate over map entries
for k,v := range m {
fmt.Printf("key (%v), value (%T : %v)\n", k, v, v)
}
}

func main() {

decodeJSON()
mapJSON()

}
go run .\main.go
main.person{Character:"Joe Miller", Actor:"", PlaceOfBirth:"Ceres", Seasons:[]int{1, 2, 3, 4}}
map[string]interface {}{"actor":"Cara Gee", "char":"Camina Drummer", "loc":"Tycho Station", "s":[]interface {}{4, 5, 6}}
key (s), value ([]interface {} : [4 5 6])
key (char), value (string : Camina Drummer)
key (actor), value (string : Cara Gee)
key (loc), value (string : Tycho Station)