Is there a way to get the current route that triggered an http.HandleFunc? Maybe something like this?
http.HandleFunc("/foo/", serveFoo)
func serveFoo(rw http.ResponseWriter, req *http.Request) {
fmt.Println(http.CurrentRoute())
// Should print "/foo/"
}
The reason I want to get the current route is because I find myself writing code like this often.
if req.URL.Path != "/some-route/" {
http.NotFound(resp, req)
return
}
// or
key := req.URL.Path[len("/some-other-route/"):]
It would be nice if the code was a bit more copy-pastable, modular, and DRY like this.
if req.URL.Path != http.CurrentRoute() {
http.NotFound(resp, req)
return
}
// or
key := req.URL.Path[http.CurrentRoute():]
This is really just a small thing, so I'd rather not bring a whole other dependency into my project (Gorilla Mux).
It is not possible to get the current route that matched, but it is possible to eliminate the duplicate code in your scenario. Write a handler that checks the path before calling through to another handler:
func HandleFuncExact(mux *http.ServeMux, pattern string, handler func(http.ResponseWriter, *http.Request) {
mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
if req.URL.Path != pattern {
http.NotFound(w, r)
return
}
handler(w, r)
})
}
In your application, call the wrapper instead of HandlFunc:
HandleFuncExact(http.DefaultServeMux, "/some-route/", serveSomeRoute)
The function serveSomeRoute can assume that the path is exactly "/some-route/".
Related
I'm trying to find a way to add a correlation/request id for logs in our project to make it easier to navigate through them and debug when some issues occur. I found this article. From the example there, there is a middleware to add the correlationID and then retrieve it in some handler function.
Middleware function:
const ContextKeyRequestID ContextKey = "requestID"
func reqIDMiddleware1(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id := uuid.New()
ctx = context.WithValue(ctx, ContextKeyRequestID, id.String())
r = r.WithContext(ctx)
log.Debugf("Incoming request %s %s %s %s", r.Method, r.RequestURI, r.RemoteAddr, id.String())
next.ServeHTTP(w, r)
log.Debugf("Finished handling http req. %s", id.String())
})
}
Handler:
const LogFieldKeyRequestID = "requestID"
func handleSomeRequest() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
reqIDRaw := ctx.Value(ContextKeyRequestID) // reqIDRaw at this point is of type 'interface{}'
reqID, ok := reqIDRaw.(string)
if !ok {
// handler error
}
// if reached here, reqID is ready to be used
// let's use it with logrus FieldLogger!
logger := log.WithField(LogFieldKeyRequestID, reqID)
// Do something, then log what you did
logger.Debugf("What I just did!")
// Do more, log more. Handle this request seriously
}
}
But I was wondering if there is a way to achieve this without having to refactor all the existing handlers and changing the logging functionality, through some automatic configuration that would add id for each log, in my case our project is quite big, and doing it in the way described above would require a lot of changes.
Have you looked at WithContext and Hooks?
You still have to modify your code but you can centralize some behaviours.
https://go.dev/play/p/4YxJMK6Zl5D
I am new to GoLang and working on my first API. I have two endpoints, and I want to rate limit only one of them. I found a helpful tutorial to get me started, and I've based my approach off of the tutorial, recognizing that this approach will rate limit both of my endpoints:
var limiter = rate.NewLimiter(rate.Every((1*time.Hour)/3), 1)
func limit(next http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
if limiter.Allow() == false {
http.Error(res, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(res, req)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", createNewToken)
mux.HandleFunc("/notify", sendPushNotificationToAllTokens)
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(mux)))
}
I researched the difference between http.Handle and http.HandleFunc and naively believed that I could substitute http.HandleFunc for http.Handle. This approach is completely flawed as the logic contained in the HandlerFunc never executes:
var limiter = rate.NewLimiter(rate.Every(1*time.Hour/3), 1)
func limit(next http.HandlerFunc) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
if limiter.Allow() == false {
http.Error(res, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(res, req)
}
}
func main() {
//mux := http.NewServeMux()
http.HandleFunc("/", createNewToken)
http.HandleFunc("/notify", sendPushNotificationToAllTokens)
// attempt to only rate limit the /notify endpoint
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(sendPushNotificationToAllTokens)))
Can anyone explain why this does not work, and how I could approach this problem to only rate limit a specific endpoint?
The distinction between using a plain http.Handler and a http.HanlderFunc doesn't really matter here. http.HandleFunc is just a way to convert a regular function into a http.Handler - it essentially does the same thing as your original version of limit.
Your implementations of limit both look fine; probably the second is better because it's simpler. Instead, the issue is in main. When you call http.ListenAndServeTLS and provide a value for the final argument, it requests that only the handler you pass in as that final argument be used as the root request handler. Any calls to http.Handle() or http.HandleFunc() are ignored unless you pass in nil as this final argument.
What you want to do instead is apply limit to the specific handler you want to limit. You have two options for this. First, you can use a ServeMux like in your first code snippet:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", createNewToken)
// Limit only the handler for "/notify".
mux.HandleFunc("/notify", limit(sendPushNotificationToAllTokens))
// Don't limit the whole mux.
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", mux))
}
Alternatively, you can do something more like your second code snippet, but pass in nil for the final argument to http.ListenAndServeTLS so that the default http.ServeMux is used, meaning that the calls to http.HandleFunc() will be respected:
func main() {
http.HandleFunc("/", createNewToken)
// Limit only the handler for "/notify".
http.HandleFunc("/notify", limit(sendPushNotificationToAllTokens))
// Pass in nil here so that http.DefaultServeMux is used.
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", nil))
}
For a simple application, the first approach is probably fine. For anything more complex, I'd recommend the later approach because it will work if you open multiple servers or do other more complex things.
Here is the complete example from my current reading material "Hands-On Restful Web Services With Go" from Packt.
func filterContentType(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Currently in the check content type middleware")
// Filtering requests by MIME type
if r.Header.Get("Content-type") != "application/json" {
w.WriteHeader(http.StatusUnsupportedMediaType)
w.Write([]byte("415 - Unsupported Media Type. Please send JSON"))
return
}
handler.ServeHTTP(w, r)
})
}
func setServerTimeCookie(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Setting cookie to every API response
cookie := http.Cookie{Name: "ServerTimeUTC", Value: strconv.FormatInt(time.Now().Unix(), 10)}
http.SetCookie(w, &cookie)
log.Println("Currently in the set server time middleware")
handler.ServeHTTP(w, r)
})
}
func handle(w http.ResponseWriter, r *http.Request) {
// Check if method is POST
if r.Method == "POST" {
var tempCity city
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&tempCity)
if err != nil {
panic(err)
}
defer r.Body.Close()
// Your resource creation logic goes here. For now it is plain print to console
log.Printf("Got %s city with area of %d sq miles!\n", tempCity.Name, tempCity.Area)
// Tell everything is fine
w.WriteHeader(http.StatusOK)
w.Write([]byte("201 - Created"))
} else {
// Say method not allowed
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("405 - Method Not Allowed"))
}
}
func main() {
originalHandler := http.HandlerFunc(handle)
http.Handle("/city", filterContentType(setServerTimeCookie(originalHandler))) // !
http.ListenAndServe(":8000", nil)
}
This program simply consists of the main function and 3 other functions, their logic is arbitrary and just copied from my book's example.
At bottom, where I've commented with "!", filterContentType is using an argument that itself is a function (setServerTimeCookie), and it looks like it's being invoked with originalHandler as its argument.
However when this code is run, the order of execution is:
filterContentType 2. setServerTimeCookie 3. originalHandler
This is counterintuitive to what I understand about using functions as arguments. I assumed that setServerTimeCookie would be the first to execute but that's not the case; it's behaving like an uninvoked function.
This leads to my question, what is causing setServerTimeCookie to defer its execution despite the syntax suggesting it's being invoked as filterContentType's argument?
I attempted to simplify things for my own understanding:
func main() {
one(two(three))
}
func one(f func()) {
fmt.Println("ONE\n")
f()
}
func two(f func()) {
fmt.Println("TWO\n")
f()
}
func three(){
fmt.Println("THREE\n")
}
This code does not build, I'm left with the error:
two(three) used as value -which tells me that two is being invoked, unlike the book's example.
What's the difference and again, why doesn't the book's example invoke setServerTimeCookie first? My only assumption is that it has something to do with the implementation of http.HandlerFunc so maybe I should start there.
Any insight to fast-forward my understanding would be greatly appreciated.
This doesn't compile because two(three) does not return a value.
I assume you want to return a function closure in this case, so to fix:
func two(f func()) func() {
return func() {
fmt.Println("TWO\n")
f()
}
}
https://go.dev/play/p/vBrAO6nwy4X
Circling back to your question about setServerTimeCookie and it's use of return http.HandlerFunc(fn). Looking at the source for http.HandlerFunc reveals it's actually a type definition - and NOT a conventional function call. It's actual IMHO the most powerful and underrated four lines of code in the go standard library:
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
By creating this value of http.HandlerFunc, it's implicitly a http.Handler, since it provides the ServeHTTP method. This therefore allows this method to be called upon request - which is exactly what a webservice is designed to do: the underlying function f will be invoked when the handler is invoked.
Because in the expression one(two(three)) function two is not passed as function reference. Instead function two is called with the argument tree, which is not what function one expects
I have created middle-wares using Adapter pattern. One of my middle-ware is for authentication. So if the user is not authorized then I have to send back response to the user and the next middle-ware/s should not be called.
// Adapter type
type Adapter func(http.Handler) http.Handler
// Adapt func
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
// Call all middleware
for _, adapter := range adapters {
h = adapter(h)
}
return h
}
// CheckAuth middleware
func CheckAuth() Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get Authorization token from the header
// Validate the token
// if valid token then call h.ServeHTTP(w, r)
// else send response 401 to the user,
if(validUser){
h.ServeHTTP(w, r)
}else{
fmt.Fprintf(w, "Unauthorized")
}
return h
}
}
}
http.Handle("/", Adapt(indexHandler, AddHeader(),
CheckAuth(),
CopyMgoSession(db),
Notify(logger),
)
in the CheckAuth middleware I'm calling h.ServeHTTP(w, r) only if the user is authorized, so for the else condtition we also need to break the for loop of the Adapt function or else it will call next middleware even after sending the response.
let me know if there is any other way to handle such situation.
The next middleware in the chain only runs if you explicitly call it.
That next middleware is passed to your closure as h, and you are calling it by invoking h.ServeHTTP(). If you do not call this, no other middleware runs, so you must supply the complete HTTP response.
The Adapt function is not relevant for serving requests. It is executed once (and only once) before the HTTP server even starts. Note that it returns an http.Handler but it isn't an http.Handler itself.
That handler that Adapt returns in this case behaves like this:
var indexHandler http.Handler
func handlerWithMiddleWare(w http.ResponseWriter, r *http.Request) {
notify := func(w http.ResponseWriter, r *http.Request) {
copyMgoSession := func(w http.ResponseWriter, r *http.Request) {
checkAuth := func(w http.ResponseWriter, r *http.Request) {
addHeader := func(w http.ResponseWriter, r *http.Request) {
indexHandler.ServeHTTP(w, r)
}
addHeader(w, r)
}
checkAuth(w, r)
}
copyMgoSession(w, r)
}
notify(w, r)
}
So if you let CheckAuth return without calling the next middleware, you can send whatever response you like; just as you would in any other handler.
By the way, you way want to let Adapt iterate in reverse order. I'm not sure that you're aware that Notify executes first, then CopyMgoSession, then CheckAuth, and then AddHeader.
Middleware is typically chained. There are frameworks that can do it for you. A sleek example is Alice.
chain := alice.New(th.Throttle, timeoutHandler, nosurf.NewPure).Then(myHandler)
If you want to do it yourself you can use recursion to avoid a for loop. For example (from this link):
// buildChain builds the middlware chain recursively, functions are first class
func buildChain(f http.HandlerFunc, m ...middleware) http.HandlerFunc {
// if our chain is done, use the original handlerfunc
if len(m) == 0 {
return f
}
// otherwise nest the handlerfuncs
return m[0](buildChain(f, m[1:cap(m)]...))
}
Each middleware receives the next as parameter. As such the next has to be manually called by the previous handler otherwise the chain stops. So in your auth middleware you don't have to call the next one if auth fails and the chain stops with your error status being the last thing returned. So in your code you need to accept a parameter of http.Handler and that is the next handler (a middleware function has to have the signature of func(http.Handler) http.Handler). See this blog for more details.
You may want to set the correct http status codes as well. Include something like this:
http.Error(w, "Forbidden: xyz", http.StatusForbidden)
I am building a Go web application that supports various middleware functions when handling routing. I'm trying to stick to net/http as much as possible and was wondering how I might accomplish this without using middleware libraries like negroni.
Essentially what I would like to do is to be able to provide a slice of middleware functions, say one for logging, one for checking for a valid JWT, and then finally the handler to handle the request.
I am able to do this with negroni fairly simply by defining the following struct:
// Route ..
type Route struct {
Method string
Path string
Middleware []negroni.Handler
Handler http.HandlerFunc
}
and then defining a route like:
var commonRoutes = []Route{
{
Method: "GET",
Path: "/info",
Middleware: []negroni.Handler{negroni.HandlerFunc(middleware.CheckCache), negroni.HandlerFunc(middleware.Authenticated), negroni.NewLogger()},
Handler: handlers.APIInfo,
},
}
Finally when I boot up my server, I import the list of routes and register them like so:
for _, r := range routes {
handler := append(r.Middleware, negroni.Wrap(r.Handler))
router.Handle(r.Path, negroni.New(handler...)).Methods(r.Method)
}
And this works perfectly.
Any idea how I might be able to do this with just the standard net/http signature and way of defining middleware handlers that look like this:
http.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
Thank you :)
func Auth(n http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("Start")
n.ServeHTTP(w, r)
log.Printf("End")
})
}
func processReq(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Success"))
}
func main() {
handler := http.HandlerFunc(processReq)
http.Handle("/",Auth(handler))
http.ListenAndServe(":8000", nil)
}
can be done using http.handler
Simple. You define each handler like so:
// So I don't have to type it over and over...
type HTTPHandler func(w http.ResponseWriter, r *http.Request)
func Handler1(next HTTPHandler) HTTPHandler {
return func(w http.ResponseWriter, r *http.Request){
// Do stuff
if next != nil {
next(w, r)
}
}
}
// Handler2 ... HandlerN defined in the same basic way.
// Chaining:
http.Handle("/", Handler1(Handler2(nil)))
Each handler takes the next handler and returns a closure that does whatever you want plus calling the next handler. If you need lots of these it may make sense to write a helper similar to this one:
func MakeHandler(worker, next HTTPHandler) HTTPHandler {
return func(w http.ResponseWriter, r *http.Request){
// Maybe have to worker return an error and do standard error
// handling here? Could simplify your code some depending on
// what you are doing.
worker(w, r)
if next != nil {
next(w, r)
}
}
}