为了账号安全,请及时绑定邮箱和手机立即绑定

golang - 从任何地方退出 http 处理程序

golang - 从任何地方退出 http 处理程序

Go
aluckdog 2023-01-03 17:18:08
我正在使用 net/http 包并想知道如何从代码中的任何位置退出处理程序。说我有这段代码:func main() {    http.HandleFunc("/", handler)    http.ListenAndServe(":8080", nil)}func handler(w http.ResponseWriter, r *http.Request){    err := checkSomeThing(w, r)    if err != nil {        return    }    fmt.Println("End of Handler.")    return}func checkSomeThing(w http.ResponseWriter, r *http.Request) error{    http.Error(w, "Bad Request!", http.StatusBadRequest)         return errors.New("bad request")}理想情况下,我想从 checkSomeThing 函数中退出处理程序,而不必返回然后再向上返回一个级别,随着应用程序的增长,这会变得更糟。这纯粹是为了代码可读性。
查看完整描述

3 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

惯用的方法是检查错误返回调用链。


要从任何地方退出处理程序,请按照 encoding/json 包中的模式使用 panic 和 recover 。


为恐慌定义一个独特的类型:


type httpError struct {

    status  int

    message string

}

编写要在 defer 语句中使用的函数。该函数检查类型并适当地处理错误。否则,函数会继续恐慌。


func handleExit(w http.ResponseWriter) {

    if r := recover(); r != nil {

        if he, ok := r.(httpError); ok {

            http.Error(w, he.message, he.status)

        } else {

            panic(r)

        }

    }

}

为调用 panic 编写一个辅助函数:


func exit(status int, message string) {

    panic(httpError{status: status, message: message})

}

使用这样的功能:


func example() {

   exit(http.StatusBadRequest, "Bad!")

}


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

   defer handleExit(w)

   example()

}


查看完整回答
反对 回复 2023-01-03
?
慕桂英4014372

TA贡献1871条经验 获得超13个赞

我的回答: 首先,在 Golang 中建立的通用模式是让错误作为返回值从被调用者“冒泡”回到调用者。它在可读性和可重用性方面具有很多优势。副作用是有很多if err != nil {return}支票。


如果你真的想打破常规,我的建议


我要提出一个想法,我认为它在 golang 编码风格和模式方面并不常见或不标准。但我没有在网上看到任何暗示这是灾难性的事情。让我们看看我在评论中说这太糟糕了。


您可以使用runtime.Goexit()来实现您想要的。处理程序只是等待另一个 goroutine 来完成工作。如果 go-routine 中运行的内部代码想要中止处理,它可以调用 Goexit()。它的优点是所有defer语句仍将执行。


这看起来像是 Golang 目前不支持的异常处理的弱版本。但我要把它扔出去。


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


    var cleanExit bool = false

    var ch = make(chan bool)


    // the actual handler implementation in a goroutine

    go func() {

        defer close(ch)

        handlerImpl(w, r)

        cleanExit = true // if handlerImpl invokes goExit, this line doesn't execute

    }()


    // wait for goroutine to exit

    <-ch


    if cleanExit {

        fmt.Println("Handler exited normally")

    } else {

        fmt.Println("Hanlder was aborted")

    }

}


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

    checkSomeThing(w, r)

}


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

    http.Error(w, "Bad Request!", http.StatusBadRequest)

    runtime.Goexit()

}


查看完整回答
反对 回复 2023-01-03
?
呼如林

TA贡献1798条经验 获得超3个赞

如果checkSomeThing()特定于该路线,您可能应该继续使用您粘贴的代码示例。

如果checkSomeThing()是所有路由(或路由子集)共有的函数,您可以选择一种方式在调用特定路由的处理程序之前运行中间件。

例如,请参阅此答案此答案,或者这是一种仅使用标准 http 包中的代码来完成此操作的方法:

func checkSomething(...) error {

    ...

}


func WrapWithCheck(handler http.Handler) http.Handler {

    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

        err := checkSomething(w, req)

        if err != nil {

            return

        }


        handler.ServeHTTP(w, req)

    })

}


func setupRouter() http.Handler {

    mux := http.NewServeMux()

    mux.HandleFunc("/foo/", handleFoo)

    mux.HandleFunc("/bar/", handleBar)

    mux.HandleFunc("/baz/", handleBaz)


    // add your common call to 'checkSomething' here :

    handler := WrapWithCheck(mux)

    return handler

}


注意:我尝试httptest在上面的操场上使用,但由于某种原因它在操场上陷入僵局。sample.go如果您将此代码复制/粘贴到一个文件中并使用,它会很好地工作go run sample.go


查看完整回答
反对 回复 2023-01-03
  • 3 回答
  • 0 关注
  • 115 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信