扩展HTTP处理程序 [英] Extending HTTP Handlers

查看:126
本文介绍了扩展HTTP处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的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
}

//确保appHandler满足http.Handler接口
func(fn appHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
如果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)

默认值:
http.Error(w,message,err.Code)




但是我不知道如何保留我现有的中间件功能/包装,它允许我这样链接中间件: r.HandleFunc(/ route,use(myHandler,middleware1,middleware2))其中使用和我的中间件如下所示:

  func use h http.HandlerFunc,中间件... func(http.HandlerFunc)http.HandlerFunc)http.HandlerFunc {
for _,m:=范围中间件{
h = m(h)
}

return h
}

func AntiCSRF(h http.HandlerFunc)http.HandlerFunc {
return http.HandlerF unc(func(w http.ResponseWriter,r * http.Request){
//做某事
// h.ServeHTTP(w,r)
}
}

从我所能得出的结果来看,它就像下面这样(不起作用)。我收到一个错误,指出不能在分配中使用m(h)(类型为http.Handler)作为类型appHandler:need type assertion 。如何解决这个问题,同时仍然保持中间件本身原样?



你可以在这里找到一个(简化的)操作示例: http://play.golang.org/p/Cmmo-wK2Af

  r.Handle(/ route,use(myHandler,middleware.NoCache))//构造示例! 

func use(h myHandlerType ?,中间件... func(http.Handler)http.Handler)http.Handler {
for _,m:=范围中间件{
h = m(h)
}

return h
}

func myHandler(w http.ResponseWriter,r * http.Request)* appError {

//非常人造的例子
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 (),max-age = 0,private,must-revalidate)
w.Header()。Set(X-Accel-Expires,0)

h.ServeHTTP(w,r)
})
}

什么是我错过了,有没有更好的方法来做到这一点?

解决方案

我已经成功解决了这个问题,这要感谢#go-坚果



该解决方案允许我使用自定义处理程序类型,链式中间件,避免重复处理处理程序(即appHandler(myHandler)),中间件...):

$ p $ type appHandler func(http.ResponseWriter,* http.Request)* appError

类型appError结构{
代码int
错误错误
}

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)
默认值:
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:=范围中间件{
res = m(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 {
$ b $是rr:= doSomething()
if err!= nil {
return& appError {500,err}
}

//呈现您的模板等。
return nil
}

使用这样的路线: r.Handle(/ route,use(myHandler,someMiddleware))



显然可以修改 appHandler 返回任何你喜欢的内容,添加额外的字段到 appError 等等。如果你想把它应用到所有路由中,你的中间件也能够包装你的路由器 - 例如 http.Handle(/,someMiddleware(r))


I have a fairly quick-and-dirty error handler in my Go web app that raises a HTTP error, logs the important parts of the response and serves an error template. I'd like to remove the repetition where I'm writing something like this a few too many times in a handler:

err := doSomething()
if err != nil {
    serverError(w, r, err, code)
}

I've had a good read of the Error Handling and Go article which covers defining a custom HTTP handler type that returns a error type/struct like this (or even returning int, err instead):

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)

        }
    }
}

But I'm not sure how to retain my existing middleware functionality/wrapper that allows me to chain middleware like this: r.HandleFunc("/route", use(myHandler, middleware1, middleware2)) where use and my middleware look like this:

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

From what I can figure, it'd be something like the below (which doesn't work). I'm getting an error saying cannot use m(h) (type http.Handler) as type appHandler in assignment: need type assertion. How do I resolve this whilst still keeping the middleware itself "as is"?

You can find a (simplified) playground example here: 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)
    })
}

What am I missing and is there a better way to do this?

解决方案

I've managed to solve this thanks to the help of 'cronos' on #go-nuts.

The solution allows me to use a custom handler type, chain middleware and avoid the repetition of having to wrap handlers (i.e. appHandler(myHandler)), middleware...):

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
}

With routes looking like this: r.Handle("/route", use(myHandler, someMiddleware))

You can obviously modify appHandler to return whatever you like, add additional fields to appError and so on. Your middleware is also able to wrap your router if you want to apply it to all routes - i.e. http.Handle("/", someMiddleware(r))

这篇关于扩展HTTP处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆