I'm using a valuable rootHandler
to deal with error generated. I'd like to know how I could use this handler for every endpoint my api may serve.
r := mux.NewRouter()
r.Handle("/api/endpoint0", rootHandler(function0)).Methods("POST")
r.HandleFunc("/api/endpoint1", function1).Methods("POST")
r.HandleFunc("/api/endpoint2", function2).Methods("POST")
s := &http.Server{
Handler: r,
Addr: "127.0.0.1:8080",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
My error handler is working as follow :
type rootHandler func(http.ResponseWriter, *http.Request) error
func (fn rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := fn(w, r) // Call handler function
if err == nil {
return
}
//log error or w/e
}
func NewHTTPError(err error, status int, detail string) error {
return &HTTPError{
Cause: err,
Detail: detail,
Status: status,
}
}
And one of the exemple function I was using :
func function0(w http.ResponseWriter, r *http.Request) (err error) {
if err = notLogged(); err != nil {
return NewHTTPError(err, 400, "not authorized")
}
}
Do I have to wrap every route functions with rootHanlder(functionName) or is there a way to apply it to every routes ?
Depending on the exact structure of your rootHandler
, you might be able to set it as a middleware:
func GetRootHandlerMW(authUser SomeType) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do what needs to be done?
next.ServeHTTP(w, r)
})
}
}
...
r.Use(GetRootHandlerMW(authUser))
A simple logging middleware looks like this:
func LogMW(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRequest(...)
next.ServeHTTP(w, r)
})
}
...
r.Use(LogMW)
From your added code, it looks like you want to change the HTTP handler function to return error. That changes the signature of the function and you cannot use it as a handler. You can either:
Adding values to a context will create a new context, so if you use context to pass values back from the handler, you have to set a placeholder first. Something like this:
type RequestError struct {
Err error
}
type requestErrorKeyType int
const requestErrorKey requestErrorKeyType=iota
func SetRequestError(req *http.Request, err error) {
if r:=req.Context().Value(requestErrorKey); r!=nil {
r.(*RequestError).Err=err
}
}
// In the handler...
requestError:=RequestError{}
newReq:=request.WithContext(request.Context().WithValue(requestErrorKey,&requestError))
next.ServeHTTP(w,newReq)
if requestError.Err!=nil {
...
}