如何终止来自golang中子函数的请求
How to terminate request from a sub function in golang
我想 return 并终止 checkSomeThing
函数中的请求。但问题是进程继续并且 return 没有响应,除非到达 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!
根据 http.Error 文档
Error replies to the request with the specified error message and HTTP code.
It does not otherwise end the request; the caller should ensure no further writes are done to w. The error message should be plain text.
因此在执行 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")
}
始终尝试在 API 级别处理错误 - 将错误冒泡到调用堆栈中。然而,在某些情况下这是不可能的——也许您的处理程序是处理程序中间件链中的众多处理程序之一,并且您不希望其他层完成。所以...
如果您想立即中止来自处理程序的请求,标准库支持 panic
和恢复。
If ServeHTTP panics, the server (the caller of ServeHTTP) assumes that
the effect of the panic was isolated to the active request. It
recovers the panic, logs a stack trace to the server error log, and
either closes the network connection or sends an HTTP/2 RST_STREAM,
depending on the HTTP protocol. To abort a handler so the client sees
an interrupted response but the server doesn't log an error, panic
with the value ErrAbortHandler.
和http.ErrAbortHandler
:
... is a sentinel panic value to abort a handler. While
any panic from ServeHTTP aborts the response to the client, panicking
with ErrAbortHandler also suppresses logging of a stack trace to the
server's error log.
因此在您的处理程序中执行此操作:
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)
}
有个问题...
对客户端的写入将被缓冲 - panic
ing 可能会导致这些缓冲的写入丢失。
至forcibly flush缓冲区:
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
panic(http.ErrAbortHandler)
或者您可以添加自己的 panic
恢复。只要确保在恢复过程中“重新提高”任何 panic
s - 如果它们不是您造成的 - 这样它们就不会丢失:
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
}
在上面的 panic-recovery 代码中,http.ServeHTTP
永远不会知道有 panic - 并自然地刷新请求缓冲区。
我想 return 并终止 checkSomeThing
函数中的请求。但问题是进程继续并且 return 没有响应,除非到达 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!
根据 http.Error 文档
Error replies to the request with the specified error message and HTTP code. It does not otherwise end the request; the caller should ensure no further writes are done to w. The error message should be plain text.
因此在执行 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")
}
始终尝试在 API 级别处理错误 - 将错误冒泡到调用堆栈中。然而,在某些情况下这是不可能的——也许您的处理程序是处理程序中间件链中的众多处理程序之一,并且您不希望其他层完成。所以...
如果您想立即中止来自处理程序的请求,标准库支持 panic
和恢复。
If ServeHTTP panics, the server (the caller of ServeHTTP) assumes that the effect of the panic was isolated to the active request. It recovers the panic, logs a stack trace to the server error log, and either closes the network connection or sends an HTTP/2 RST_STREAM, depending on the HTTP protocol. To abort a handler so the client sees an interrupted response but the server doesn't log an error, panic with the value ErrAbortHandler.
和http.ErrAbortHandler
:
... is a sentinel panic value to abort a handler. While any panic from ServeHTTP aborts the response to the client, panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log.
因此在您的处理程序中执行此操作:
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)
}
有个问题...
对客户端的写入将被缓冲 - panic
ing 可能会导致这些缓冲的写入丢失。
至forcibly flush缓冲区:
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
panic(http.ErrAbortHandler)
或者您可以添加自己的 panic
恢复。只要确保在恢复过程中“重新提高”任何 panic
s - 如果它们不是您造成的 - 这样它们就不会丢失:
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
}
在上面的 panic-recovery 代码中,http.ServeHTTP
永远不会知道有 panic - 并自然地刷新请求缓冲区。