Created
January 13, 2021 07:36
-
-
Save judwhite/a862543d0dd1b82a91d56eadbaca04aa to your computer and use it in GitHub Desktop.
Gorilla Mux Example v2
This file contains 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
package main | |
import ( | |
"encoding/json" | |
"log" | |
"net/http" | |
"sync" | |
"time" | |
"github.com/gorilla/mux" | |
) | |
const MaxPostBytes = 128 | |
func main() { | |
const listenAddr = ":8080" | |
apiController := NewController() | |
router := apiController.Router() | |
log.Printf("Listening on %s...\n", listenAddr) | |
log.Fatal(http.ListenAndServe(listenAddr, router)) | |
} | |
type Controller struct { | |
message string | |
messageMtx sync.RWMutex | |
} | |
func NewController() *Controller { | |
return &Controller{ | |
message: "hello! try POST'ing to /api/message with a body like {\"message\":\"hello world!\"}", | |
} | |
} | |
func (c *Controller) Router() *mux.Router { | |
r := mux.NewRouter() | |
api := r.PathPrefix("/api").Subrouter() | |
api.HandleFunc("/ping", c.handleGetPing).Methods("GET") | |
api.HandleFunc("/message", c.handleGetMessage).Methods("GET") | |
api.HandleFunc("/message", c.handlePostMessage).Methods("POST") | |
return r | |
} | |
func (*Controller) handleGetPing(w http.ResponseWriter, _ *http.Request) { | |
type getVersionResponse struct { | |
Message string `json:"message"` | |
EpochMilliseconds int64 `json:"epoch_ms"` | |
} | |
ms := time.Now().UnixNano() / 1e6 | |
resp := getVersionResponse{Message: "pong", EpochMilliseconds: ms} | |
JSON(w, resp) | |
} | |
func (c *Controller) handleGetMessage(w http.ResponseWriter, _ *http.Request) { | |
type getMessageResponse struct { | |
Message string `json:"message"` | |
} | |
c.messageMtx.RLock() | |
msg := c.message | |
c.messageMtx.RUnlock() | |
resp := getMessageResponse{Message: msg} | |
JSON(w, resp) | |
} | |
func (c *Controller) handlePostMessage(w http.ResponseWriter, r *http.Request) { | |
type postMessageRequest struct { | |
Message string `json:"message"` | |
} | |
// limit the amount the client can POST | |
r.Body = http.MaxBytesReader(w, r.Body, MaxPostBytes) | |
// read JSON from request body | |
var req postMessageRequest | |
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { | |
// note: don't write arbitrary errors to the client in production | |
Error(w, err.Error(), http.StatusBadRequest) | |
return | |
} | |
// validate request | |
if req.Message == "" { | |
Error(w, "message is empty", http.StatusBadRequest) | |
return | |
} | |
// do the update | |
c.messageMtx.Lock() | |
c.message = req.Message | |
c.messageMtx.Unlock() | |
// respond to the client | |
type postMessageResponse struct { | |
Status string `json:"status"` | |
} | |
JSON(w, postMessageResponse{Status: "OK"}) | |
} | |
func JSON(w http.ResponseWriter, data interface{}) { | |
if err := json.NewEncoder(w).Encode(data); err != nil { | |
// note: don't write arbitrary errors to the client in production | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
} | |
} | |
type ErrorResponse struct { | |
Error string `json:"error"` | |
} | |
func Error(w http.ResponseWriter, error string, statusCode int) { | |
w.WriteHeader(statusCode) | |
JSON(w, ErrorResponse{Error: error}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment