I have a Context struct that is used for the Application Context. The ConfigureRouter function receives the context as a parameter and sets the global variable c so middleware in the same file can use it.
var c *core.Context
func ConfigureRouter(ctx *core.Context, router *httprouter.Router) {
c = ctx //make context available in all middleware
router.POST("/v1/tokens/create", token.Create) //using httprouter
}
The one route listed above calls token.Create ( from the token package which is a sub-directory) but it needs the context too.
//token/token.go
func Create(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
//Help! I need the context to do things
}
How can I pass the context to the token package?
You can redefine your Create function as a function that returns the handler function:
func Create(ctx *core.Context) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
// Now you have the context to do things
}
}
Where httprouter.Handle is a func type defined by httprouter to be type Handle func(http.ResponseWriter, *http.Request, Params).
And then you would use it like this in ConfigureRouter:
func ConfigureRouter(ctx *core.Context, router *httprouter.Router) {
router.POST("/v1/tokens/create", token.Create(ctx))
}
Related
hi i am trying tr write unit test for a post request using
"net/http"
"net/http/httptest"
if in my handler i have extra parameter other than w http.ResponseWriter, r *http.Request like db , kafka, etc
func ResponseHandler(w http.ResponseWriter, r *http.Request, db *dynamo.DB, p *Confluent.Producer)
how can i do
handler := http.HandlerFunc(handlername) any idea ?
I m getting
***cannot convert ResponseHandler (type func(http.ResponseWriter, http.Request, dynamo.DB, "github.com/confluentinc/confluent-kafka-go/kafka".Producer)) to type http.HandlerFunc
Your handler needs to implement the http.Handler signature, meaning it cannot have other arguments or return anything.
The way to provide it with other arguments is to have them come from an outer scope. So, assuming your not-really-a-Handler looks like:
func ResponseHandler(w http.ResponseWriter, r *http.Request, db *dynamo.DB, p *Confluent.Producer)
...you will need to generate an actual Handler calling your not-really-a-Handler. Supposing you already obtained the DB and Producer in your router initialization function, you can route like this:
var db *dynamo.DB = someDBFactory()
var p *Confluent.Producer = giveMeAProducer()
responseHandler := func(w http.ResponseWriter, r *http.Request) {
return ResponseHandler(w, r, db, p)
})
http.HandleFunc(somePath, responseHandler)
You could also pass the parameters explicitly instead of relying on the outer scope, using a higher-order function:
var db *dynamo.DB := someDBFactory()
var p *Confluent.Producer := giveMeAProducer()
newResponseHandler := func(db *dynamo.DB, p *Confluent.Producer) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return ResponseHandler(w, r, db, p)
})
})
http.HandleFunc(somePath, newResponseHandler(db, p))
Wrap the HandlerFunc with your custom function as
func ResponseHandler(w http.ResponseWriter, r *http.Request, db *dynamo.DB, p *Confluent.Producer)func(http.ResponseWriter, *http.Request){
return func(http.ResponseWriter, *http.Request){
// your code goes here
}}
I may wrong, but the two "extra" parameters (db *dynamo.DB and p *Confluent.Producer) don't seem request-specific. If the handler depends on them to handle requests, you could make those dependencies available to the handler in several ways. Seasoned Gophers tend to "inject" dependencies through a method receiver.
You could declare a custom struct type whose fields are the dependencies needed by your handler(s):
type API struct {
db *dynamo.DB
p *Confluent.Producer
}
You can drop the extra two parameters and turn your ResponseHandler function into a method on your custom API type, thereby making it compatible with the http.HandlerFunc type:
func (api *API) ResponseHandler(w http.ResponseWriter, r *http.Request) {
// ...
}
Finally, you only have to wire the dependencies in an API variable in main.
I already have a web server and it work fine, now I want to add the fileSystem module to it.
OverView of the project under below.
// Custom router
type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request) error
type Middleware func(Handler) Handler
type App struct {
mux *chi.Mux
och *ochttp.Handler
mw []Middleware
}
func NewApp(mw ...Middleware) *App {
app := App{
mux: chi.NewRouter(),
mw: mw,
}
app.och = &ochttp.Handler{
Handler: app.mux,
Propagation: &tracecontext.HTTPFormat{},
}
return &app
}
func (a *App) Handle(verb, path string, handler Handler, mw ...Middleware) {
handler = wrapMiddleware(a.mw, handler)
h := func(w http.ResponseWriter, r *http.Request) {
...
}
a.mux.MethodFunc(verb, path, h)
}
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.och.ServeHTTP(w, r)
}
// route
func API(...) http.Handler {
app := web.NewApp(mid.Logger(log)...)
s := Something{
...
}
app.Handle("GET", "/v1/somthing", s.DoSomething)
return app
}
// Handler
type Something struct {
...
}
func (s *Something) DoSomething(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
...
}
// main
api := http.Server{
...
Handler: API(...),
}
go func() {
api.ListenAndServe()
}()
The app struct is a custom router, it contains 3rd router, trace library and some middleware. The type Handler is the specific Handler format using for Middleware and any api handler register to this router. Because this project hard code only one Handler, what's the best to add another Handler like fileSystem to it?
http.Handler and Handler conversion through closure.
// http.Handler to Handler
func NewHandler(h http.Handler) Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
h.ServeHTTP(w, r)
}
}
// Handler to http.Handler
func NewHTTPHandler(h Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h(context.Background(), w, r)
})
}
// or Handler implement http.Handler
func (fn Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fn(context.Background(), w, r)
}
app.Handle("ANY","/fs/*", NewHandler(http.FileServer(http.Dir("public"))))
type Handler func (Context) is the best Handler definition. Request the context.Context object through http.Request.Context() or the top-level context.Context is obtained through the http.Server.BaseContext property.
Middleware's best implementation idea has two methods used by the echo or gin framework, but the echo method should not copy the memory allocation waste.
It is recommended to implement the router by yourself. In the mainstream framework and routing inventory, does not have routing priority, such as a large number of libraries such as echo gin httprouter gorilla/mux.
The following is the framework and the simplest implementation that I designed for 22 months.
my web framework: https://github.com/eudore/eudore
simple framework: https://github.com/eudore/eudore/wiki/3.2-frame-mirco-web
I need to wrap the GetAssetsCompute function inside a middleware
r.Handle("/api/v1/assets/ComputeBlade", GetAssetsCompute(assetService)).Methods("GET")
func GetAssetsCompute(assetService ServiceType) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// stuff here
})
}
but because middlewares take HTTP handlers as an argument and my function is not a handler, I can't.
I was thinking of doing something like this.
func GetAssetsCompute(assetService ServiceType) http.Handler {
return MyMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// stuff here
}))
}
func MyMiddleware(next http.Handler) http.Handler {
}
Is this correct? Or is there a better way to do this.
Also inside the middleware, I need to access the URL endpoint, do some processing and store this processed value and then again access that in the main handler. How can I do that?
EDIT: I want to apply this middleware to only a subset(>1) of endpoints I have. Not all
I also require the assetService variable used in GetAssetsCompute(assetService ServiceType) function in the handler. So, I need this closure too.
It seems you are trying to do 2 things. 1 - Apply a middleware to only some of your request handlers. 2 - Pass data from your middleware to your request handlers.
For the first one, I can think of three options. The first is what you are doing now, having a Middleware function in which you wrap your handler functions when you pass them to r.Handle. Pseudocode:
r.Handle("/path1", Mware(Handler1())).Methods("GET")
r.Handle("/path2", Mware(Handler2())).Methods("GET")
r.Handle("/path3-nomiddleware", Handler3()).Methods("GET")
The second thing you could do is to add code to your middleware to filter based on URI path and then register your middleware using r.Use. Pseudocode:
const mwarePaths []string = ...
func Mware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.RequestURI is in mwarePaths {
// do the middleware
}
}
}
r.Use(Mware)
Thirdly, you could put the code in a function which you call directly in your handlers and not register it like a middleware. Pseudocode:
func myUtil(w http.ResponseWriter, r *http.Request){ ... }
func GetAssetsCompute(assetService ServiceType) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
myUtil(w, r)
// stuff here
})
}
For the second thing - passing data from middleware to request handlers - here are some ideas.
First, if you go with the regular-function, no-middleware setup above, this problem disappears because anything you need in your handler can simply be a return value from your function.
If you do use a middleware, you can use the context library (also from gorilla) to tie variables to an http.Request instance for passing to your handler: http://www.gorillatoolkit.org/pkg/context . Using that looks like this:
import "github.com/gorilla/context"
func middleware(...) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
context.Set(r, "myKey", "bar")
}
}
func handler(w http.ResponseWriter, r *http.Request) {
val, ok := context.GetOk(r, "myKey") // returns "bar", true
}
Which of these options you choose to use is up to you (you know your needs). But, as mentioned in the comments, a good rule of thumb would be that code which handles unrelated concerns to what your request handlers do can be middleware. Code which handles concerns that are directly related to what your request handlers are doing can go directly in the handlers.
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)
}
}
}
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.