Clean up JSON responses with responder library
This commit is contained in:
parent
cf23018d70
commit
e5018c38c2
|
@ -1,17 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/tomwright/queryparam/v4"
|
||||
)
|
||||
|
||||
func httpErr(w http.ResponseWriter, code int) {
|
||||
http.Error(w, http.StatusText(code), code)
|
||||
}
|
||||
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package responder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type defaultResponse struct {
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
value *interface{}
|
||||
status int
|
||||
pretty bool
|
||||
}
|
||||
|
||||
type Option func(r *response)
|
||||
|
||||
func Pretty(pretty bool) Option {
|
||||
return func(r *response) {
|
||||
r.pretty = pretty
|
||||
}
|
||||
}
|
||||
|
||||
func Status(status int) Option {
|
||||
return func(r *response) {
|
||||
r.status = status
|
||||
}
|
||||
}
|
||||
|
||||
func Body(v interface{}) Option {
|
||||
return func(r *response) {
|
||||
r.value = &v
|
||||
}
|
||||
}
|
||||
|
||||
func Respond(w http.ResponseWriter, opts ...Option) {
|
||||
r := &response{
|
||||
status: http.StatusOK,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(r)
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
|
||||
if r.pretty {
|
||||
enc.SetIndent(" ", "")
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(r.status)
|
||||
|
||||
if v := r.value; v != nil {
|
||||
_ = enc.Encode(*v)
|
||||
} else {
|
||||
_ = enc.Encode(&defaultResponse{
|
||||
Status: r.status,
|
||||
Message: http.StatusText(r.status),
|
||||
})
|
||||
}
|
||||
}
|
97
main.go
97
main.go
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/tomwright/queryparam/v4"
|
||||
"github.com/zikaeroh/codies/internal/protocol"
|
||||
"github.com/zikaeroh/codies/internal/responder"
|
||||
"github.com/zikaeroh/codies/internal/server"
|
||||
"github.com/zikaeroh/codies/internal/version"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
@ -83,19 +84,18 @@ func main() {
|
|||
r.Use(middleware.NoCache)
|
||||
|
||||
r.Get("/api/time", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&protocol.TimeResponse{Time: time.Now()})
|
||||
responder.Respond(w, responder.Body(&protocol.TimeResponse{Time: time.Now()}))
|
||||
})
|
||||
|
||||
r.Get("/api/stats", func(w http.ResponseWriter, r *http.Request) {
|
||||
rooms, clients := srv.Stats()
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
_ = enc.Encode(&protocol.StatsResponse{
|
||||
responder.Respond(w,
|
||||
responder.Body(&protocol.StatsResponse{
|
||||
Rooms: rooms,
|
||||
Clients: clients,
|
||||
})
|
||||
}),
|
||||
responder.Pretty(true),
|
||||
)
|
||||
})
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
|
@ -106,18 +106,16 @@ func main() {
|
|||
r.Get("/api/exists", func(w http.ResponseWriter, r *http.Request) {
|
||||
query := &protocol.ExistsQuery{}
|
||||
if err := queryparam.Parse(r.URL.Query(), query); err != nil {
|
||||
httpErr(w, http.StatusBadRequest)
|
||||
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
|
||||
room := srv.FindRoomByID(query.RoomID)
|
||||
if room == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
responder.Respond(w, responder.Status(http.StatusNotFound))
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
responder.Respond(w, responder.Status(http.StatusOK))
|
||||
}
|
||||
|
||||
_, _ = w.Write([]byte("."))
|
||||
})
|
||||
|
||||
r.Post("/api/room", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -125,76 +123,88 @@ func main() {
|
|||
|
||||
req := &protocol.RoomRequest{}
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
httpErr(w, http.StatusBadRequest)
|
||||
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
if msg, valid := req.Valid(); !valid {
|
||||
resp := &protocol.RoomResponse{
|
||||
responder.Respond(w,
|
||||
responder.Status(http.StatusBadRequest),
|
||||
responder.Body(&protocol.RoomResponse{
|
||||
Error: stringPtr(msg),
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp := &protocol.RoomResponse{}
|
||||
|
||||
var room *server.Room
|
||||
if req.Create {
|
||||
room, err := srv.CreateRoom(req.RoomName, req.RoomPass)
|
||||
var err error
|
||||
room, err = srv.CreateRoom(req.RoomName, req.RoomPass)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case server.ErrRoomExists:
|
||||
resp.Error = stringPtr("Room already exists.")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
responder.Respond(w,
|
||||
responder.Status(http.StatusBadRequest),
|
||||
responder.Body(&protocol.RoomResponse{
|
||||
Error: stringPtr("Room already exists."),
|
||||
}),
|
||||
)
|
||||
case server.ErrTooManyRooms:
|
||||
resp.Error = stringPtr("Too many rooms.")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
responder.Respond(w,
|
||||
responder.Status(http.StatusServiceUnavailable),
|
||||
responder.Body(&protocol.RoomResponse{
|
||||
Error: stringPtr("Too many rooms."),
|
||||
}),
|
||||
)
|
||||
default:
|
||||
resp.Error = stringPtr("An unknown error occurred.")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
responder.Respond(w,
|
||||
responder.Status(http.StatusInternalServerError),
|
||||
responder.Body(&protocol.RoomResponse{
|
||||
Error: stringPtr("An unknown error occurred."),
|
||||
}),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
resp.ID = &room.ID
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
} else {
|
||||
room := srv.FindRoom(req.RoomName)
|
||||
room = srv.FindRoom(req.RoomName)
|
||||
if room == nil || room.Password != req.RoomPass {
|
||||
resp.Error = stringPtr("Room not found or password does not match.")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
resp.ID = &room.ID
|
||||
w.WriteHeader(http.StatusOK)
|
||||
responder.Respond(w,
|
||||
responder.Status(http.StatusNotFound),
|
||||
responder.Body(&protocol.RoomResponse{
|
||||
Error: stringPtr("Room not found or password does not match."),
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
responder.Respond(w, responder.Body(&protocol.RoomResponse{
|
||||
ID: &room.ID,
|
||||
}))
|
||||
})
|
||||
|
||||
r.Get("/api/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
query := &protocol.WSQuery{}
|
||||
if err := queryparam.Parse(r.URL.Query(), query); err != nil {
|
||||
httpErr(w, http.StatusBadRequest)
|
||||
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
|
||||
if _, valid := query.Valid(); !valid {
|
||||
httpErr(w, http.StatusBadRequest)
|
||||
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
|
||||
room := srv.FindRoomByID(query.RoomID)
|
||||
if room == nil {
|
||||
httpErr(w, http.StatusNotFound)
|
||||
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||
return
|
||||
}
|
||||
|
||||
c, err := websocket.Accept(w, r, wsOpts)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -258,7 +268,6 @@ func checkVersion(next http.Handler) http.Handler {
|
|||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
c, err := websocket.Accept(w, r, wsOpts)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
c.Close(4418, reason)
|
||||
|
|
Loading…
Reference in New Issue