Closed sttyru closed 5 years ago
I want to get something like that (it's a plot only):
func main() {
...
s := query();
r := httprouter.New();
r.GET("/", GetAnIndex, s );
}
...
func GetAnIndex(w http.ResponseWriter, r *http.Request, _ httprouter.Params, s *Settings) {
....
fmt.Println(s.Path);
...
}
Hello, are you using Go 1.7+ ? I could give you and example using the context
package. And no, you cannot change the the interface type httprouter.Handle func(http.ResponseWriter, *http.Request, Params)
Hello, @falmar! Thank you! Can you share an example?
Yes. I'm using go 1.7.4.
go version go1.7.4 linux/amd64
I encourage you to use the standard http.Handler func (w http.ResponseWriter, r *http.Request)
instead of httprouter.Handle which may change in favor of the context
package.
This would be a standard http.Handler
func getIndex(w http.ResponseWriter, r *http.Request) {
//...
// ugh... but here we don't have access to s
fmt.Println(s.Path);
//...
}
Well s
does't exist in the scope and besides where the hell is ps httprouter.Params? lets fix that!
First you would need to wrap julien's httprouter.Handle
to take the Params
, put them in the context and then call your http.Handler
.
The wrapper receives a http.Handler
and returns a httprouter.Handle
to satisfy httprouter
func wrapHandler(h http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Take the context out from the request
ctx := r.Context()
// Get new context with key-value "params" -> "httprouter.Params"
ctx = context.WithValue(ctx, "params", ps)
// Get new http.Request with the new context
r = r.WithContext(ctx)
// Call your original http.Handler
h.ServeHTTP(w, r)
}
}
You now can take the params out in your GetIndex handler
func getIndex(w http.ResponseWriter, r *http.Request) {
//...
// You can take the params this way
ps, ok := r.Context().Value("params").(httprouter.Params)
if !ok {
log.Fatal("ps is not type httprouter.Params")
}
//...
}
I ignore where your Settings are coming from, but there could be two approaches, use a middleware to pass the settings down the context
or use a wrapper similar to the func wrapHandler
1) Use a wrapper
func getIndexWithSettings(s Settings) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//
// Settings is in the scope
fmt.Println(s.Path)
//
})
}
func getIndexWithSettings2(s Settings) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//
// Settings is in the scope and ps httprouter.params
fmt.Println(s.Path)
//
}
}
2) Use a middleware
func settingsMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Take the context out from the request
ctx := r.Context()
// Get the settings
s := somewhere()
// Get new context with key-value "settings"
ctx = context.WithValue(ctx, "settings", s)
// Get new http.Request with the new context
r = r.WithContext(ctx)
// Call your original http.Handler
h.ServeHTTP(w, r)
})
}
This is how you would use any of this approaches
func main() {
r := httprouter.New()
// wrapHandler
r.GET("/index", wrapHandler(http.HandlerFunc(getIndex)))
// inject settings
r.GET("/index", getIndexWithSettings(s))
// middleware
r.GET("/index", wrapHandler(settingsMiddleware(http.HandlerFunc(getIndex))))
http.ListenAndServe(addr, r)
}
But chained all this handlers and wrappers will be cumbersome you could use Alice
to fix that
@falmar
Thank you for the comprehensive answer! You are guru of Go
!
But in my sorry I can't to build it :-( Help me, please, again. Sorry, I know it's rude.
package main
import (
"fmt"
"context"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
r := httprouter.New()
var s = "/var/bin"
r.GET("/index", getIndexWithSettings(s))
http.ListenAndServe(":8080", r)
}
func wrapHandler(h http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Take the context out from the request
ctx := r.Context()
ctx = context.WithValue(ctx, "params", ps)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
}
}
func getIndex(w http.ResponseWriter, r *http.Request) {
ps, ok := r.Context().Value("params").(httprouter.Params)
if !ok {
fmt.Println("ps is not type httprouter.Params")
}
}
func getIndexWithSettings(s string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println(s)
})
}
/*
func settingsMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var s = "/var/bin"
ctx = context.WithValue(ctx, "params", s)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
})
}
*/
Message error:
cannot use getIndexWithSettings(s) (type http.Handler) as type httprouter.Handle in argument to r.GET
Ok, look func getIndexWithSettings
is returning http.Handler
, it does not satisfy httprouter.Get(string, httprouter.Handle)
change this func
func getIndexWithSettings(s string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println(s)
})
}
for this
func getIndexWithSettings(s string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Println(s)
}
}
And you will end up with this
package main
import (
"fmt"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
r := httprouter.New()
var s = "/var/bin"
r.GET("/index", getIndexWithSettings(s))
http.ListenAndServe(":8080", r)
}
func getIndexWithSettings(s string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Println(s)
}
}
You should take a look at the middleware approach as a better practice when writing future programs
package main
import (
"context"
"fmt"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
r := httprouter.New()
index := settingsMiddleware(http.HandlerFunc(getIndex))
r.GET("/index", wrapHandler(index))
http.ListenAndServe(":8080", r)
}
func wrapHandler(h http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Take the context out from the request
ctx := r.Context()
ctx = context.WithValue(ctx, "params", ps)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
}
}
func settingsMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var s = "/var/bin"
ctx := r.Context()
ctx = context.WithValue(ctx, "settings", s)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
})
}
func getIndex(w http.ResponseWriter, r *http.Request) {
s, ok := r.Context().Value("settings").(string)
if !ok {
fmt.Println("s is not type string")
}
fmt.Println(s) // "/var/bin"
}
Thank you very much for your help! I've modified my source code and HTTP router do everything it takes! :) Thank you!
Now I want to force drop a connection, but that's another story.
Hello!
My issue were not necessarily linked with julienschmidt/httprouter but I sure you can to help me. I try to write a little application at Go for simulate a behaviour of some legacy HTTP-backends. My choice fell on julienschmidt/httprouter because this tool have an impressive performance and I found any pretty examples. But I have a trouble by the one part of code. I need to pass the variable to 'handler'. I have an huge JSON-like structure filled by a data from the database. I need to use it in whole or in part. Of cource, I can to call the function for re-filling a structure at the everyone HTTP-request but it's too slow. I found some mentions about 'wrappers' for function but I don't know how to apply this. Besides, I'm guess that not everything is what it appears on the surface. Could you kindly share a code for newbies like a me, please? :)
Thank you!