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)