Skip to content

Instantly share code, notes, and snippets.

@judwhite
Created January 13, 2021 07:36
Show Gist options
  • Save judwhite/a862543d0dd1b82a91d56eadbaca04aa to your computer and use it in GitHub Desktop.
Save judwhite/a862543d0dd1b82a91d56eadbaca04aa to your computer and use it in GitHub Desktop.
Gorilla Mux Example v2
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