I have a requirement of informing user the allowed methods for specific endpoint. This information will be shown in case there is 405 response from server (I'm using gorilla/mux).
I've trying using custom handler by mux for 405, but I can't find any info in the Request object and ResponseWriter.
After reading the docs and SO, I can't find any. May I know if anyone has been doing same thing before?
Code is below. I only allow GET apparently.
router.HandleFunc("/users/{id}",).Methods(http.MethodGet)
In my handler for 405, the response header is empty apparently. There is no info on allowed methods for this endpoint.
func MethodNotAllowedHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Debugln("Header Writer: ", w.Header())
})
Thanks!
You can modify your MethodNotAllowed handler such below;
It simply walks through the routes and applies routematch function. If path is matched and method is not, it returns mux.ErrMethodMismatch error. Then you can obtain allowed methods for the route and send it in the headers
func notAllowedHandler(x *mux.Router) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//next.ServeHTTP(w, r)
x.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var routeMatch mux.RouteMatch
if route.Match(r, &routeMatch) || routeMatch.MatchErr == mux.ErrMethodMismatch {
m, _ := route.GetMethods()
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m, ", "))
}
return nil
})
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
})
}
Edit: I forgot to mention. You need to pass router parameter to your custom method not allowed handler;
You can find the complete example below;
func main() {
r := mux.NewRouter()
r.HandleFunc("/test", func(rw http.ResponseWriter, r *http.Request) {}).Methods(http.MethodPost)
r.HandleFunc("/test2", func(rw http.ResponseWriter, r *http.Request) {}).Methods(http.MethodPut)
r.MethodNotAllowedHandler = notAllowedHandler(r)
http.ListenAndServe(":8089", r)
}
func notAllowedHandler(x *mux.Router) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
x.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var routeMatch mux.RouteMatch
if route.Match(r, &routeMatch) || routeMatch.MatchErr == mux.ErrMethodMismatch {
m, _ := route.GetMethods()
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m, ", "))
}
return nil
})
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
})
}
Related
Using this https://github.com/praveen001/go-passport
Path with passsport middleware:
app.Put("/api/auth/login", p.Authenticate("local", MyHandler))
The passport authenticate function:
// Authenticate calls `Strategy.Authenticate` method of registered strategies, and checks the `passport.Result` returned by it.
//
// The result is stored in the request context with `passport.CtxKey` as key.
//
func (p *Passport) Authenticate(name string, h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s, ok := p.Options.strategies[name]
if !ok {
w.WriteHeader(404)
return
}
s.Authenticate(w, r, func(res *Result) {
res.StrategyName = name
ctx := context.WithValue(r.Context(), CtxKey, res)
h.ServeHTTP(w, r.WithContext(ctx))
})
}
}
So this attach the response from my auth method and returns a http.HandleFunc which I can define myself (MyHandler).
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Where is the data attached from the Authenticate func?
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(401)
io.WriteString(w, `{"status":"ok"}`)
}
I dont understand where I can reach the attached data from the Authenticate func. The comment says: "//The result is stored in the request context with passport.CtxKey as key".
I don't find it in the r *http.Request.
The http.Request object has a Context() method which gives you the context. You access the key through that.
E.g.
ctx := r.Context()
value := ctx.Value(passport.CtxKey)
I am fairly new to Go and have not been able to find any information on this, maybe it is just not possible at this time.
I am trying to delete or replace a mux route (using http.NewServeMux, or gorilla's mux.Router). My end goal is to be able to enable/disable a route or set of routes without having to restart the program.
I can probably accomplish this on a handler to handler basis and just return 404 if that feature is "disabled", but I would rather find a more general way to do this since I would like to implement it for every route in my application.
Or would I be better off just keeping track of disabled url patterns and using some middleware to prevent handler execution?
If someone can at least point me in the right direction, I will absolutely post code examples of a solution assuming there is one. Thanks!
There's no built in way, but it is easy enough to implement play.
type HasHandleFunc interface { //this is just so it would work for gorilla and http.ServerMux
HandleFunc(pattern string, handler func(w http.ResponseWriter, req *http.Request))
}
type Handler struct {
http.HandlerFunc
Enabled bool
}
type Handlers map[string]*Handler
func (h Handlers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if handler, ok := h[path]; ok && handler.Enabled {
handler.ServeHTTP(w, r)
} else {
http.Error(w, "Not Found", http.StatusNotFound)
}
}
func (h Handlers) HandleFunc(mux HasHandleFunc, pattern string, handler http.HandlerFunc) {
h[pattern] = &Handler{handler, true}
mux.HandleFunc(pattern, h.ServeHTTP)
}
func main() {
mux := http.NewServeMux()
handlers := Handlers{}
handlers.HandleFunc(mux, "/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this will show once"))
handlers["/"].Enabled = false
})
http.Handle("/", mux)
http.ListenAndServe(":9020", nil)
}
Yes you can.
One way to do it is to have a sturct that implement http.Handle interface with the method
ServeHTTP.
Then have the struct contain another muxer like gorilla's
and finally have an atomic Switch to enable/ disable the subrouting
This is a working example of what I mean:
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
"sync/atomic"
)
var recording int32
func isRecording() bool {
return atomic.LoadInt32(&recording) != 0
}
func setRecording(shouldRecord bool) {
if shouldRecord {
atomic.StoreInt32(&recording, 1)
} else {
atomic.StoreInt32(&recording, 0)
}
}
type SwitchHandler struct {
mux http.Handler
}
func (s *SwitchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if isRecording() {
fmt.Printf("Switch Handler is Recording\n")
s.mux.ServeHTTP(w, r)
return
}
fmt.Printf("Switch Handler is NOT Recording\n")
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "NOT Recording\n")
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/success/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Recording\n")
})
handler := &SwitchHandler{mux: router}
setRecording(false)
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
According to https://github.com/gorilla/mux/issues/82 it is suggested to swap the router instead of deleting routes. Existing connections will stay open.
I'm building an application using the Micro Service Architecture.
On the gateway, I do want to route requests to the correct endpoints.
But, the endpoint are now known at runtime and needs to be configured in the DB.
Below if the code to get the router.
func getRouter() *mux.Router {
r := mux.NewRouter()
r.Use(dynamicRouteMiddleware)
return r
}
The middleware itself is this:
func dynamicRouteMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Error")
})
}
However, the "Error" is never printed.
It's only printed when I put a handler for '/'
How can I create middleware without handlers?
It's called "middleware" because it's supposed to put your Handler in the "middle". It receives the input before your Handler, and receives the output of your Handler.
Inherently, to have your middleware work, you are required to have at least one Handler. Preferably you may just use this functionality that you need in the Handler rather than middleware.
On the middleware, you need call the next handler so all incoming requests will proceed to the destination route.
func dynamicRouteMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Error")
next.ServeHTTP(w, r) // <------- this one
})
}
You can register any routes as you want, but in the very end make sure the r object used as handler of / route.
r.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("test"))
})
r.HandleFunc("/test/12", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("test 12"))
})
r.HandleFunc("/about-us", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("about us"))
})
http.Handle("/", r)
http.ListenAndServe(":8080", nil)
When you access /test, /test/12, or /about-us; the Error will still be printed.
Previously it's not printed because you don't proceed to the next handler. The code next.ServeHTTP(w, r) is mandatory in your case.
If I were to use the DefaultServeMux (which I designate by passing nil as the second argument to ListenAndServe), then I have access to http.HandleFunc, which you see used below in this example from the Go wiki:
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
In my current code, I am not able to use the DefaultServeMux i.e. I'm passing a custom handler to ListenAndServe
h := &mypackage.Handler{
Database: mydb
}
http.ListenAndServe(":8080", h)
so I don't get the http.HandleFunc built in. However, I have to adapt some authorization code to my code base that requires something like http.HandleFunc. For example, if I had been using DefaultServeMux, when I hit the "/protected" route, I would want to go to the Protected handler, but only after passing through the h.AuthorizationHandlerFunc like this
h.AuthorizationHandlerFunc(Protected)
However, since I'm not using DefaultServeMux, it's not working i.e. I'm not able to pass the Protected function (and have it called) to the AuthorizationHandlerFunc. This is the implementation of the AuthorizationHandlerFunc below. You can see below that Protected never gets called.
Question: how do I implement HandlerFunc in this situation (without using DefaultServeMux)?
func (h *Handler) AuthorizationHandlerFunc(next http.HandlerFunc) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
h.AuthorizationMiddleWare(w, r, next)
})
}
func (h *Handler) AuthorizationMiddleWare(w http.ResponseWriter, r *http.Request, next http.HandlerFunc){
//other stuff happens
log.Println("this is never getting called")
next(w,r)
}
func (h *Handler)Protected(w http.ResponseWriter, r *http.Request){
log.Println("this is never getting called")
}
Update
ServeHTTP is implemented on mypackage.Handler. Why is the Protected function not getting called, or, for that matter, the relevant code in the AuthorizationMiddleWare?
Re-implement your authorization middleware as a http.Handler :
type auth struct {
DB *sql.DB
UnauthorizedHandler http.Handler
}
func NewAuth(db *sql.DB, unauthorized http.Handler) *auth {
return auth{db, unauthorized}
}
func (a *auth) Protected(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// Check whether the request is valid
// If it's invalid, call your error func and make sure to *return* early!
if !valid {
a.UnauthorizedHandler.ServeHTTP(w, r)
return
}
// Call the next handler on success
h.ServeHTTP(w, r)
return
}
return http.HandlerFunc(fn)
}
func someHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello!\n")
}
func main() {
auth := NewAuth(db, errorHandler)
r := http.NewServeMux()
// We have a http.Handler implementation that wraps a http.HandlerFunc
// ... so we call r.Handle on our ServeMux and type-cast the wrapped func
r.Handle("/protected", auth.Protected(http.HandlerFunc(someHandler)))
// Just a simple http.HandlerFunc here
r.HandleFunc("/public", someOtherHandler)
log.Fatal(http.ListenAndServe(":8000", r))
}
Take a look at the httpauth lib I wrote for a different example with a ServeHTTP method. Both the above and explicitly creating a ServeHTTP method on your type are valid approaches.
I am fairly new to Go and have not been able to find any information on this, maybe it is just not possible at this time.
I am trying to delete or replace a mux route (using http.NewServeMux, or gorilla's mux.Router). My end goal is to be able to enable/disable a route or set of routes without having to restart the program.
I can probably accomplish this on a handler to handler basis and just return 404 if that feature is "disabled", but I would rather find a more general way to do this since I would like to implement it for every route in my application.
Or would I be better off just keeping track of disabled url patterns and using some middleware to prevent handler execution?
If someone can at least point me in the right direction, I will absolutely post code examples of a solution assuming there is one. Thanks!
There's no built in way, but it is easy enough to implement play.
type HasHandleFunc interface { //this is just so it would work for gorilla and http.ServerMux
HandleFunc(pattern string, handler func(w http.ResponseWriter, req *http.Request))
}
type Handler struct {
http.HandlerFunc
Enabled bool
}
type Handlers map[string]*Handler
func (h Handlers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if handler, ok := h[path]; ok && handler.Enabled {
handler.ServeHTTP(w, r)
} else {
http.Error(w, "Not Found", http.StatusNotFound)
}
}
func (h Handlers) HandleFunc(mux HasHandleFunc, pattern string, handler http.HandlerFunc) {
h[pattern] = &Handler{handler, true}
mux.HandleFunc(pattern, h.ServeHTTP)
}
func main() {
mux := http.NewServeMux()
handlers := Handlers{}
handlers.HandleFunc(mux, "/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this will show once"))
handlers["/"].Enabled = false
})
http.Handle("/", mux)
http.ListenAndServe(":9020", nil)
}
Yes you can.
One way to do it is to have a sturct that implement http.Handle interface with the method
ServeHTTP.
Then have the struct contain another muxer like gorilla's
and finally have an atomic Switch to enable/ disable the subrouting
This is a working example of what I mean:
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
"sync/atomic"
)
var recording int32
func isRecording() bool {
return atomic.LoadInt32(&recording) != 0
}
func setRecording(shouldRecord bool) {
if shouldRecord {
atomic.StoreInt32(&recording, 1)
} else {
atomic.StoreInt32(&recording, 0)
}
}
type SwitchHandler struct {
mux http.Handler
}
func (s *SwitchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if isRecording() {
fmt.Printf("Switch Handler is Recording\n")
s.mux.ServeHTTP(w, r)
return
}
fmt.Printf("Switch Handler is NOT Recording\n")
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "NOT Recording\n")
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/success/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Recording\n")
})
handler := &SwitchHandler{mux: router}
setRecording(false)
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
According to https://github.com/gorilla/mux/issues/82 it is suggested to swap the router instead of deleting routes. Existing connections will stay open.