0

我想返回并终止checkSomeThing功能中的请求。但问题是进程继续进行,除非到达main(). 这是我的代码:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)
    // ...

    http.ListenAndServe(":8080", nil)
}

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

    http.Error(w, "Operation completed!", http.StatusOK)
    fmt.Println("End of Handler.")
}

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

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

运行程序后,输出如下:

// Output:    
< HTTP/1.1 400 Bad Request
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Sun, 12 Sep 2021 12:38:49 GMT
< Content-Length: 34
< 
Bad Request!
Operation completed!
4

2 回答 2

2

始终尝试在 API 级别处理错误 - 在调用堆栈中冒泡错误。但是,在某些情况下这是不可能的——也许您的处理程序是处理程序中间件链中的众多处理程序之一,而您不希望完成其他层。所以...


如果您想立即中止来自处理程序的请求,标准库支持panic& 恢复。

http.Handler 文档

如果 ServeHTTP 发生恐慌,服务器(ServeHTTP 的调用者)假定恐慌的影响与活动请求隔离。它恢复恐慌,将堆栈跟踪记录到服务器错误日志,然后关闭网络连接或发送 HTTP/2 RST_STREAM,具体取决于 HTTP 协议。要中止处理程序以使客户端看到中断的响应但服务器不记录错误,请使用值 ErrAbortHandler 恐慌。

http.ErrAbortHandler

...是中止处理程序的哨兵恐慌值。虽然任何来自 ServeHTTP 的恐慌都会中止对客户端的响应,但使用 ErrAbortHandler 的恐慌也会抑制将堆栈跟踪记录到服务器的错误日志中。

所以要在你的处理程序中做到这一点:

func checkSomeThing(w http.ResponseWriter, r *http.Request) {
    http.Error(w, "Bad Request!", http.StatusBadRequest)
    panic(http.ErrAbortHandler) // terminate request (and any more handlers in the chain)
}

有一个问题...

对客户端的写入将被缓冲 - 并且panicing 可能会导致这些缓冲的写入丢失。

强制刷新缓冲区:

if f, ok := w.(http.Flusher); ok {
    f.Flush()
}

panic(http.ErrAbortHandler)

或者您可以添加自己的panic恢复。只要确保在恢复过程中“重新提高”任何panics - 如果它们不是由您引起的 - 这样它们就不会丢失:

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

    defer func() {
        r := recover()

        switch r {

        case nil: // no panic

        // (2) goes here...
        case http.ErrAbortHandler:
            log.Println("Recovered in handler")

        // (2) or here...
        default:
            panic(r) // re-raise any unexpected panic
        }
    }()
    checkSomeThing(w, r)  // (1) panic here ...

    // (3) ... and this never runs
}

在上面的恐慌恢复代码中,http.ServeHTTP永远不会知道有恐慌 - 并自然地刷新请求缓冲区。

于 2021-09-12T15:14:37.213 回答
1

根据 http.Error 文档

错误使用指定的错误消息和 HTTP 代码回复请求。它不会以其他方式结束请求;调用者应确保不再对 w 进行写入。错误消息应该是纯文本。

因此,在执行该checkSomeThing 函数后,它只需将错误字符串写入responsewriter并继续处理进一步的操作。

您的代码的工作版本如下所示:

package main

import (
    "errors"
    "fmt"
    "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
    }

    http.Error(w, "Operation completed!", http.StatusOK)
    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")
}
于 2021-09-12T12:53:13.230 回答