Go: HTTP server
Date
Author
Implement an HTTP server that has three routes and maintains a database of the world’s largest lakes.
- The first route runs a handler postHandler which accepts a POST request with JSON-encoded lake information and posts it to the database.
{ "type": "post", "payload": {"id": "id00", "name": "Name of the lake", "area": 452001} }
- The second route runs a handler deleteHandler which deletes the lake from the database by id. If the payload id is not present in the data set, the server returns a 404 response.
{ "type": "delete", "payload": "id00" }
- The third route runs a handler getHandler which takes a lake from the database by id and returns it to the caller which prints the name and the area of the lake. If the id is not found in the database, the server returns a string message “404 Not Found”. Otherwise, it returns the payload object corresponding to the id.
{ "type": "get", "payload": "id00" }
Implement the server which will be queried by the program and print the output to STDOUT.
Note:
The program uses these structs:
type Lake struct {
Name string
Area int32
}
type Action struct {
Type string
Payload string
}
The base URL is contained in the global variable address. Data is stored under the store variable which is the map[string] Lake.
Constraints
- The total number of queries does not exceed 1000.
- The name and id strings consist of no more than 100 lowercase and uppercase English characters only.
- All the post ids are unique.
SOLUTION:
package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func postHandler(w http.ResponseWriter, req *http.Request) {
var lake Lake
err := json.NewDecoder(req.Body).Decode(&lake)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
store[lake.Id] = lake
w.WriteHeader(http.StatusOK)
}
func deleteHandler(w http.ResponseWriter, req *http.Request) {
id := req.URL.Query().Get("id")
if id == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
_, ok := store[id]
if !ok {
w.WriteHeader(http.StatusNotFound)
return
}
delete(store, id)
w.WriteHeader(http.StatusOK)
}
func getHandler(w http.ResponseWriter, req *http.Request) {
id := req.URL.Query().Get("id")
if id == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
lake, ok := store[id]
if !ok {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "404 Not Found")
return
}
response, err := json.Marshal(lake)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(response)
}
func main() {
reader := bufio.NewReaderSize(os.Stdin, 16*1024*1024)
actionsCount, err := strconv.ParseInt(strings.TrimSpace(readLine(reader)), 10, 64)
checkError(err)
http.HandleFunc("/get", getHandler)
http.HandleFunc("/post", postHandler)
http.HandleFunc("/delete", deleteHandler)
go http.ListenAndServe(portSuffix, nil)
time.Sleep(100 * time.Millisecond)
var actions []string
for i := 0; i < int(actionsCount); i++ {
actionsItem := readLine(reader)
actions = append(actions, actionsItem)
}
for _, actionStr := range actions {
var action Action
err := json.Unmarshal([]byte(actionStr), &action)
checkError(err)
switch action.Type {
case "post":
_, err := http.Post(address+"/post", "application/json", strings.NewReader(action.Payload))
checkError(err)
case "delete":
client := &http.Client{}
req, err := http.NewRequest("DELETE", address+"/delete?id="+action.Payload, nil)
checkError(err)
resp, err := client.Do(req)
checkError(err)
if resp.StatusCode != http.StatusOK {
fmt.Printf("%s\n", resp.Status)
continue
}
case "get":
resp, err := http.Get(address + "/get?id=" + action.Payload)
checkError(err)
if resp.StatusCode != http.StatusOK {
fmt.Printf("%s\n", resp.Status)
continue
}
var lake Lake
err = json.NewDecoder(resp.Body).Decode(&lake)
checkError(err)
fmt.Printf("%s\n", lake.Name)
fmt.Printf("%d\n", lake.Area)
}
}
fmt.Printf("\n")
}
const portSuffix = ":3333"
var address = "http://127.0.0.1" + portSuffix
type Lake struct {
Id string `json:"id"`
Name string `json:"name"`
Area int32 `json:"area"`
}
type Action struct {
Type string
Payload string
}
var store = map[string]Lake{}
func readLine(reader *bufio.Reader) string {
str, _, err := reader.ReadLine()
if err == io.EOF {
return ""
}
return strings.TrimRight(string(str), "\r\n")
}
func checkError(err error) {
if err != nil {
panic(err)
}
}