Go lang seems a strong choice for developing web services. So I decided to learn it and try some hands on. These are my concise notes while learning it.
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
x int, y int
x, y int
bool, string, int and uint with 8,16,32,64, also uintptr. byte //alias for uint8 rune // alias for int32 (represents a Unicode code point) float32 float64 complex64 and complex128
int, uint types are automatically 32 bits or 64 bits as per the system on which it is running.
i := 42
f := float64(i)
The basic for loop has three components separated by semicolons:
for i := 0; i < 10; i++ {
sum += i
}
if x < 0 {
return "Less than 0"
}
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
var a [10]int // declares a variable a as an array of ten integers
a[low : high]
a := make([]int, 0, 5) // len(a)=0, cap(a)=5
var s []int
s = append(s, 0) // s=[0]
s = append(s, 5) // s=[0,5]
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
Insert or update an element in map m:
m[key] = elem
Retrieve an element:
elem = m[key]
Delete an element:
delete(m, key)
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
Go does not have classes.
A method is a function with a special receiver argument.
Simple difference between function and method is
foo(a) is a function
a.foo() is a method
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs()) // 50
}
(value, type)
interface{}
t := i.(T)
var i interface{} = "hello"
s := i.(string)
fmt.Println(s) // hello
s, ok := i.(string)
fmt.Println(s, ok) // hello true
f, ok := i.(float64)
fmt.Println(f, ok) // 0 false
// If type is int then do this else if type is string then do this and so on...
switch v := i.(type) {
case T:
// here v has type T
case S:
// here v has type S
default:
// no match; here v has the same type as i
}
// List represents a singly-linked list that holds values of any type.
type List[T any] struct {
next *List[T]
val T
}
goroutines
and channels
to make sure that all the different parts of the program are working together and communicating with each other correctly.Create a project folder let's say gin-web-service
cd into it and run command go mod init gin-web-service
This will create a go.mod file to manage dependencies.
Create main.go
and setup the data structure with dummy data
package main
// album represents data about a record album.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// albums slice to seed record album data.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
getAlbums
function// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
Context.JSON
instead of Context.IndentedJSON
for more compact JSON. IndentedJSON is easier to debug and read.main
handler functionfunc main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.Run("localhost:9000")
}
import (
"net/http"
"github.com/gin-gonic/gin"
)
go get .
to download all the packages.go run .
to run the code in current directory.curl http://localhost:8080/albums
and check the response.package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// album represents data about a record album.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// albums slice to seed record album data.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
// postAlbums adds an album from JSON received in the request body.
func postAlbums(c *gin.Context) {
var newAlbum album
// Call BindJSON to bind the received JSON to newAlbum.
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// Add the new album to the slice.
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
// getAlbumByID locates the album whose ID value matches the id parameter sent by the client, then returns that album as a response.
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// Loop through the list of albums, looking for
// an album whose ID value matches the parameter.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
This example will show how to store data in session cookies using the popular gorilla/sessions package in Go.
// sessions.go
package main
import (
"fmt"
"net/http"
"github.com/gorilla/sessions"
)
var (
// key must be 16, 24 or 32 bytes long (AES-128, AES-192 or AES-256)
key = []byte("super-secret-key")
store = sessions.NewCookieStore(key)
)
func secret(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "cookie-name")
// Check if user is authenticated
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
// Print secret message
fmt.Fprintln(w, "The cake is a lie!")
}
func login(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "cookie-name")
// Authentication goes here
// ...
// Set user as authenticated
session.Values["authenticated"] = true
session.Save(r, w)
}
func logout(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "cookie-name")
// Revoke users authentication
session.Values["authenticated"] = false
session.Save(r, w)
}
func main() {
http.HandleFunc("/secret", secret)
http.HandleFunc("/login", login)
http.HandleFunc("/logout", logout)
http.ListenAndServe(":8080", nil)
}