我的Go Web应用程序中有一个相当快捷的错误处理程序,它会引发HTTP错误,记录响应的重要部分并提供错误模板。我想删除在处理程序中多次编写类似代码的重复:
err := doSomething() if err != nil { serverError(w, r, err, code) }
我已经阅读了Error Handling and Go文章,其中定义了一个自定义HTTP处理程序类型,该类型将返回这样的错误类型/结构(甚至返回int,err):
type appHandler func(http.ResponseWriter, *http.Request) *appError type appError struct { code int Err error } // Ensures appHandler satisfies the http.Handler interface func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := fn(w, r); err != nil { switch err.Code { case http.StatusNotFound: http.NotFound(w, r) case http.StatusInternalServerError: http.Error(w, "message", http.StatusInternalServerError) default: http.Error(w, "message", err.Code) } } }
但是我不确定如何保留现有的中间件功能/包装,使我可以像这样链接中间件:r.HandleFunc("/route", use(myHandler, middleware1, middleware2))where use和我的中间件看起来像这样:
r.HandleFunc("/route", use(myHandler, middleware1, middleware2))
use
func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc { for _, m := range middleware { h = m(h) } return h } func AntiCSRF(h http.HandlerFunc) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // do something // h.ServeHTTP(w,r) } }
据我所知,它会像下面这样(不起作用)。我说错了cannot use m(h) (type http.Handler) as type appHandler in assignment: need type assertion。我如何解决这个问题,同时又保持中间件本身“不变”?
cannot use m(h) (type http.Handler) as type appHandler in assignment: need type assertion
您可以在此处找到一个(简化的)游乐场示例:http : //play.golang.org/p/Cmmo-wK2Af
r.Handle("/route", use(myHandler, middleware.NoCache)) // Contrived example! func use(h myHandlerType?, middleware ...func(http.Handler) http.Handler) http.Handler { for _, m := range middleware { h = m(h) } return h } func myHandler(w http.ResponseWriter, r *http.Request) *appError { // Extremely contrived example name := "Matt" _, err := fmt.Fprintf(w, "Hi %s", name) if err != nil { return &appError{500, err} } return nil } func contrivedMiddleware(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "max-age=0, private, must-revalidate") w.Header().Set("X-Accel-Expires", "0") h.ServeHTTP(w, r) }) }
我缺少什么,还有更好的方法吗?
感谢#go-nuts上的“ cronos”帮助,我设法解决了这一问题。
该解决方案允许我使用自定义处理程序类型,链中间件,并避免重复包装处理程序(即appHandler(myHandler),中间件…):
type appHandler func(http.ResponseWriter, *http.Request) *appError type appError struct { Code int Error error } func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if e := fn(w, r); e != nil { switch e.Code { case http.StatusNotFound: notFound(w, r) case http.StatusInternalServerError: serverError(w, r, e.Error, e.Code) default: serverError(w, r, e.Error, e.Code) } } } func use(h appHandler, middleware ...func(http.Handler) http.Handler) http.Handler { var res http.Handler = h for _, m := range middleware { res = m(res) } return res } func someMiddleware(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "max-age=0, private, must-revalidate") w.Header().Set("X-Accel-Expires", "0") h.ServeHTTP(w, r) }) } func myHandler(w http.ResponseWriter, r *http.Request) *appError { err := doSomething() if err != nil { return &appError{500, err} } // render your template, etc. return nil }
路线如下所示: r.Handle("/route", use(myHandler, someMiddleware))
r.Handle("/route", use(myHandler, someMiddleware))
您显然可以进行修改appHandler以返回所需的内容,并向其添加其他字段appError等等。如果要将路由器应用于所有路由,则中间件也可以包装路由器-即http.Handle("/", someMiddleware(r))
appHandler
appError
http.Handle("/", someMiddleware(r))