gpt4 book ai didi

go - 扩展 HTTP 处理程序

转载 作者:IT王子 更新时间:2023-10-29 01:13:06 26 4
gpt4 key购买 nike

我的 Go 网络应用程序中有一个相当快速和肮脏的错误处理程序,它会引发 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)) 其中 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。我该如何解决这个问题,同时仍然保持中间件本身“原样”?

您可以在这里找到一个(简化的) Playground 示例: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))

显然,您可以修改 appHandler 以返回您喜欢的任何内容,向 appError 添加额外的字段等等。如果您想将中间件应用于所有路由,您的中间件也可以包装您的路由器 - 即 http.Handle("/", someMiddleware(r))

关于go - 扩展 HTTP 处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21496093/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com