2.4 KiB
2.4 KiB
HTTP Handler Style Guide
HTTP Handler
- Each handler should implement
http.Handler
- This can be done by embedding a
httprouter.Router
(a light weight HTTP router that supports variables in the routing pattern and matches against the request method)
- This can be done by embedding a
- Required services should be exported on the struct
// ThingHandler represents an HTTP API handler for things.
type ThingHandler struct {
// embedded httprouter.Router as a lazy way to implement http.Handler
*httprouter.Router
ThingService platform.ThingService
AuthorizationService platform.AuthorizationService
Logger *zap.Logger
}
HTTP Handler Constructor
- Routes should be declared in the constructor
// NewThingHandler returns a new instance of ThingHandler.
func NewThingHandler() *ThingHandler {
h := &ThingHandler{
Router: httprouter.New(),
Logger: zap.Nop(),
}
h.HandlerFunc("POST", "/api/v2/things", h.handlePostThing)
h.HandlerFunc("GET", "/api/v2/things", h.handleGetThings)
return h
}
Route handlers (http.HandlerFunc
s)
- Each route handler should have an associated request struct and decode function
- The decode function should take a
context.Context
and an*http.Request
and return the associated route request struct
type postThingRequest struct {
Thing *platform.Thing
}
func decodePostThingRequest(ctx context.Context, r *http.Request) (*postThingRequest, error) {
t := &platform.Thing{}
if err := json.NewDecoder(r.Body).Decode(t); err != nil {
return nil, err
}
return &postThingRequest{
Thing: t,
}, nil
}
- Route
http.HandlerFuncs
should separate the decoding and encoding of HTTP requests/response from actual handler logic
// handlePostThing is the HTTP handler for the POST /api/v2/things route.
func (h *ThingHandler) handlePostThing(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
req, err := decodePostThingRequest(ctx, r)
if err != nil {
EncodeError(ctx, err, w)
return
}
// Do stuff here
if err := h.ThingService.CreateThing(ctx, req.Thing); err != nil {
EncodeError(ctx, err, w)
return
}
if err := encodeResponse(ctx, w, http.StatusCreated, req.Thing); err != nil {
h.Logger.Info("encoding response failed", zap.Error(err))
return
}
}
http.HandlerFunc
's that require particular encoding of http responses should implement an encode response function