从切片中嵌套函数

huangapple go评论74阅读模式
英文:

Nest Functions From Slice

问题

我正在构建一个支持各种中间件函数的 Go Web 应用程序,用于处理路由。我尽量使用 net/http,想知道如何在不使用像 negroni 这样的中间件库的情况下实现这一点。

基本上,我想做的是能够提供一个中间件函数的切片,比如一个用于记录日志,一个用于检查有效的 JWT,最后是处理请求的处理程序。

我可以使用 negroni 简单地实现这一点,定义如下结构体:

// Route ..
type Route struct {
  Method     string
  Path       string
  Middleware []negroni.Handler
  Handler    http.HandlerFunc
}

然后像这样定义一个路由:

var commonRoutes = []Route{
	{
		Method:     "GET",
		Path:       "/info",
		Middleware: []negroni.Handler{negroni.HandlerFunc(middleware.CheckCache), negroni.HandlerFunc(middleware.Authenticated), negroni.NewLogger()},
		Handler:    handlers.APIInfo,
	},
}

最后,当我启动服务器时,我导入路由列表并进行注册,像这样:

for _, r := range routes {
    
  handler := append(r.Middleware, negroni.Wrap(r.Handler))
    
  router.Handle(r.Path, negroni.New(handler...)).Methods(r.Method)
}

这样可以完美地工作。

有没有办法只使用标准的 net/http 签名和定义中间件处理程序的方式来实现这一点呢?

谢谢 从切片中嵌套函数

英文:

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 从切片中嵌套函数

答案1

得分: 1

可以使用http.Handler来完成翻译:

func Auth(n http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
      log.Printf("开始")
      n.ServeHTTP(w, r)
      log.Printf("结束")
  })
}

func processReq(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("成功"))
}


func main() {
  handler := http.HandlerFunc(processReq)

  http.Handle("/",Auth(handler))
  http.ListenAndServe(":8000", nil)
}

希望这可以帮助到你!

英文:
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

答案2

得分: 0

简单。你可以像这样定义每个处理程序:

// 为了不重复输入...
type HTTPHandler func(w http.ResponseWriter, r *http.Request)

func Handler1(next HTTPHandler) HTTPHandler {
    return func(w http.ResponseWriter, r *http.Request){
        // 做一些事情
        
        if next != nil {
            next(w, r)
        }
    }
}

// Handler2 ... HandlerN 以相同的基本方式定义。

// 链接:
http.Handle("/", Handler1(Handler2(nil)))

每个处理程序接受下一个处理程序,并返回一个闭包,该闭包执行你想要的操作,并调用下一个处理程序。如果你需要很多这样的处理程序,可能有必要编写一个类似于以下示例的辅助函数:

func MakeHandler(worker, next HTTPHandler) HTTPHandler {
    return func(w http.ResponseWriter, r *http.Request){
        // 可能需要让 worker 返回一个错误,并在这里进行标准错误处理?
        // 根据你的具体情况,这可能会简化你的代码。
        worker(w, r)
        
        if next != nil {
            next(w, r)
        }
    }
}
英文:

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)
        }
    }
}

huangapple
  • 本文由 发表于 2017年9月19日 13:46:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/46292705.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定