Golang Refresher :: url & http
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)