恐慌:最后一个参数必须为http.HandlerFunc类型

时间:2018-12-20 05:27:50

标签: go

我有这个辅助函数,可以正常编译:

func Middleware(adapters ...interface{}) http.HandlerFunc {

    log.Info("length of adapters:", len(adapters))

    if len(adapters) < 1 {
        panic("Adapters need to have length > 0.");
    }

    h, ok := (adapters[len(adapters)-1]).(http.HandlerFunc)

    if ok == false {
        panic("Last argument needs to be of type http.HandlerFunc") // ERROR HERE
    }

    adapters = adapters[:len(adapters)-1]

    for _, adapt := range adapters {
        h = (adapt.(AdapterFunc))(h)
    }

    return h

}

我这样称呼它:

router.HandleFunc("/share", h.makeGetMany(v)).Methods("GET")

func (h Handler) makeGetMany(v Injection) http.HandlerFunc {
    return mw.Middleware(
        mw.Allow("admin"),
        func(w http.ResponseWriter, r *http.Request) {
            log.Println("now we are sending response.");
            json.NewEncoder(w).Encode(v.Share)
        },
    )
}

问题是我遇到此错误,我无法弄清原因:

    panic: Last argument needs to be of type http.HandlerFunc

    goroutine 1 [running]:
    huru/mw.Middleware(0xc420083d40, 0x2, 0x2, 0xc42011f3c0)
            /home/oleg/codes/huru/api/src/huru/mw/middleware.go:301 +0x187
    huru/routes/share.Handler.makeGetMany(0xc4200ae1e0, 0x10)
            /home/oleg/codes/huru/api/src/huru/routes/share/share.go:62 +0x108

它确实确认适配器切片的长度为2:

 length of adapters:2

有人知道为什么这种类型的断言在这种情况下会失败吗?没有意义。也许我实际上不是在检索切片的最后一个参数或其他内容?有没有更好的方法可以将最后一个参数弹出切片?

2 个答案:

答案 0 :(得分:3)

您需要使用mw.Middleware()http.Handler语句的第二个参数包装为http.HandlerFunc()类型。

func (h Handler) makeGetMany(v Injection) http.HandlerFunc {
    return mw.Middleware(
        mw.Allow("admin"),
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            log.Println("now we are sending response.");
            json.NewEncoder(w).Encode(v.Share)
        }),
    )
}

答案 1 :(得分:1)

在http包中, ListenAndServe具有以下签名

func ListenAndServe(addr string, handler Handler) error

其中Handler(即http.Handler)是接口

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

对于一个Web应用程序,只有一个ListenAndServe调用,因此只有一个handler,它需要处理所有访问点,例如/url1/url2等等。如果我们从头开始编写handler,则该实现可能是一个自定义的struct,它环绕数据库句柄。它的ServeHTTP方法检查访问点,并将相应的内容写入ResponseWriter,这很繁琐和混杂。

这就是ServeMux的动机,它就是您代码中的router。它具有ServeHTTP方法,因此它满足http.Handler接口,并且可以用于ListenAndServe。此外,它具有HandleFunc方法来处理各个访问点

func (mux *ServeMux) HandleFunc(pattern string,
                                handler func(ResponseWriter, *Request))

请注意,此处的handler不是http.Handler,即它没有ServeHTTP方法。不必这样做,因为mux已经拥有ServeHTTP,并且其ServeHTTP方法可以将单个访问点请求分派到相应的处理程序。

请注意,它还有一个Handle方法,该方法要求参数满足http.Handler接口。与HandleFunc方法相比,使用起来不太方便。

func (mux *ServeMux) Handle(pattern string, handler Handler)

现在回到您的问题,因为您调用router.HandleFunc,所以它的输入不必是http.Handler。因此,另一种解决方案是使用func(ResponseWriter, *Request)作为中间件和makeGetMany方法的返回类型。 (中间件中的类型断言也需要更新,可能还需要更新更多代码)

@xpare的解决方案是进行类型转换,以便所有功能签名都匹配,即将func(ResponseWriter, *Request)转换为http.HandlerFunc。看看它如何工作也很有趣。从implementation

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

您会看到它基本上定义了一个ServeHTTP方法来调用自身。