Write an HTTP server that functions as a key/value database. Users will make a POST request to /db to create or assign a value. User will make a GET request /db/ to get the value. Both request and response will be in JSON format. Use a map from string to the empty interface to hold values. And since we can't access the Go data structure from two different Go routines, limit the access to the map with a sync.Mutex.
Created
February 23, 2019 17:21
-
-
Save Sequoia/ee57bf7a2cbd435642ed03c65ec1aefb to your computer and use it in GitHub Desktop.
My solution to the Lynda.com exercise
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Key Value Server | |
package main | |
import ( | |
"fmt" | |
"log" | |
"net/http" | |
"encoding/json" | |
"sync" | |
"strings" | |
) | |
type KVSet struct{ | |
Key string `json:"key"` | |
Value interface{} `json:"value"` | |
} | |
type KVGet struct{ | |
Key string `json:"key"` | |
} | |
type KVResponse = struct{ | |
Value interface{} `json:"value"` | |
Error string `json:"error"` | |
} | |
// Database | |
var db = make(map[string]interface{}) | |
var dbMux = &sync.Mutex{} | |
// POST handler | |
func setHandler(w http.ResponseWriter, r *http.Request) { | |
defer r.Body.Close() | |
// parse request | |
reqPair := &KVSet{} | |
if err := json.NewDecoder(r.Body).Decode(reqPair); err != nil{ | |
http.Error(w, err.Error(), http.StatusBadRequest) | |
return | |
} | |
dbMux.Lock() | |
// set/upate value | |
db[reqPair.Key] = reqPair.Value | |
dbMux.Unlock() | |
w.WriteHeader(http.StatusOK) | |
return | |
} | |
// GET handler | |
func getHandler(w http.ResponseWriter, r *http.Request) { | |
resp := &KVResponse{} | |
out := json.NewEncoder(w) | |
key := strings.Trim(r.URL.Path,"/") | |
// get value | |
dbMux.Lock() | |
v, ok := db[key] | |
dbMux.Unlock() | |
// if !ok, set response header 404 | |
if !ok{ | |
resp.Error = fmt.Sprintf("key %q not found", key) | |
w.WriteHeader(http.StatusNotFound) | |
if err := out.Encode(resp); err != nil{ | |
log.Printf("Can't encode %v - %s", resp, err) | |
} | |
return | |
} | |
// marshal response | |
resp.Value = v | |
// respond with { error : "", status : "" } | |
if err := out.Encode(resp); err != nil { | |
// Can't return error to client here | |
log.Printf("can't encode %v - %s", resp, err) | |
} | |
} | |
func rootHandler(w http.ResponseWriter, r *http.Request) { | |
switch r.Method { | |
case http.MethodGet: | |
getHandler(w, r) | |
case http.MethodPost: | |
setHandler(w, r) | |
default: | |
http.Error( | |
w, | |
fmt.Sprintf("method %s not supported", r.Method), | |
http.StatusBadRequest, | |
) | |
} | |
} | |
func main() { | |
http.HandleFunc("/", rootHandler) | |
if err := http.ListenAndServe(":8080", nil); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment