To be an better Gopher, get your hands dirty. Topcoder offered a serials of challenges for learning Golang.
In this blog, I tried to implement "Go Learning Challenge - Simple Web-API Server"[1].
What's used in this challenge ? Following aspects and packages will be covered in this challenge
- How to create a HTTP server: net/http
- How to create routers (handlers for different URLs): regex + url
- How to write HTTP responses: net/http
- How to load JSON config: json + io/ioutil
- How to parse URL query string: url
- How to encode data in sha256/base64: sha256/base64 + strings
- How to write tests in Golang: testing
- How to parse command line arguments: flag
- How to log: log
Configuration File example:
[ { "domain": "topcoder.com", "users": [ { "username": "takumi", "password": "ilovego" }, { "username": "teru", "password": "ilovejava" }, { "username": "toshi", "password": "iloveapex" } ] }, { "domain": "appirio.com", "users": [ { "username": "jun", "password": "ilovetopcoder" }, { "username": "narinder", "password": "ilovesamurai" }, { "username": "chris", "password": "ilovesushi" } ] } ]
config.go
package SimpleWebAPIServer import ( "io/ioutil" "encoding/json" "errors" ) type User struct { UserName string `json:"username"` Password string `json:"password"` } type Domain struct { Name string `json:"domain"` Users UserList `json:"users"` } type DomainWarehouse []Domain type UserList []User func ReadConfig(fn string) (DomainWarehouse, error) { data, err := ioutil.ReadFile(fn) if err != nil { return nil, err } //fmt.Println(string(data)) var m DomainWarehouse json.Unmarshal(data, &m) return m, nil } func (dw DomainWarehouse) GetDomain(name string) (*Domain, error) { for _, domain := range dw { if domain.Name == name { return &domain, nil } } return nil, errors.New("Failed to find domain") } func (ul UserList) GetUser(username string) (*User, error) { for _, user := range ul { if user.UserName == username { return &user, nil } } return nil, errors.New("Failed to find user") }
tests for config.go
package SimpleWebAPIServer import ( "testing" "fmt" ) func TestReadConfig(t *testing.T) { dw, err := ReadConfig("./users.json") if err != nil { fmt.Println(err) t.Error("Failed to read config") } if dw == nil || len(dw) != 2 { t.Error("Failed to unmarshal json objects") } if dw[0].Name != "topcoder.com" || len(dw[0].Users) != 3 { t.Error("Incorrect value") } } func TestGetDomain(t *testing.T) { dw, err := ReadConfig("./users.json") if err != nil { fmt.Println(err) t.Error("Failed to read config") } domain, err := dw.GetDomain("topcoder.com") if err != nil { fmt.Println(err) t.Error("Failed to get domain") } if domain.Name != "topcoder.com" || len(domain.Users) != 3 { t.Error("Incorrect value") } } func TestGetUser(t *testing.T) { dw, err := ReadConfig("./users.json") if err != nil { fmt.Println(err) t.Error("Failed to read config") } domain, err := dw.GetDomain("topcoder.com") if err != nil { fmt.Println(err) t.Error("Failed to get domain") } if domain.Name != "topcoder.com" || len(domain.Users) != 3 { t.Error("Incorrect value") } ul := domain.Users u, err := ul.GetUser("takumi") if err != nil { t.Error("Failed to get user") } if u.UserName != "takumi" || u.Password != "ilovego" { t.Error("Invalid user values") } }
After implementing this simple web api server, I got better understanding of Golang syntax and Web API stuffs. For more Web API server challenges, go to [3] about OAuth2.
[1] topcoder : http://www.topcoder.com/challenge-details/30046011/?type=develop&noncache=true
[2] git : https://coding.net/u/huys03/p/SimpleWebAPIServer/git
[3] next challenge: http://www.topcoder.com/challenge-details/30046224/?type=develop