英文:
How to terminate request from a sub function in golang
问题
我想在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!
请注意,我只翻译了你提供的代码部分。
英文:
I want to return and terminate request that comes in checkSomeThing
function. But the problem is the process continues and doesn't return response unless reaches the end of the main()
. here's my code:
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
}
After run the program here's the output:
// 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!
答案1
得分: 2
始终尝试在API级别处理错误-将错误向上冒泡到调用堆栈。然而,有些情况下这是不可能的-也许你的处理程序是处理程序链中的一个,你不希望其他层完成。所以...
如果你想立即中止处理程序的请求,标准库支持panic
和recovery
。
如果ServeHTTP发生panic,服务器(ServeHTTP的调用者)假设panic的影响仅限于活动请求。它会恢复panic,将堆栈跟踪记录到服务器错误日志,并根据HTTP协议关闭网络连接或发送HTTP/2 RST_STREAM。为了中止处理程序,使客户端看到中断的响应但服务器不记录错误,请使用值ErrAbortHandler进行panic。
以及http.ErrAbortHandler
:
...是一个用于中止处理程序的特殊panic值。虽然来自ServeHTTP的任何panic都会中止对客户端的响应,但是使用ErrAbortHandler进行panic还会抑制将堆栈跟踪记录到服务器错误日志中。
所以在你的处理程序中可以这样做:
func checkSomeThing(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Bad Request!", http.StatusBadRequest)
panic(http.ErrAbortHandler) // 终止请求(以及链中的其他处理程序)
}
这里有一个注意事项...
对客户端的写入将被缓冲-而panic
可能会导致这些缓冲的写入丢失。
要强制刷新缓冲区:
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
panic(http.ErrAbortHandler)
或者你可以添加自己的panic
恢复。只需确保在恢复过程中“重新引发”任何panic
-如果它们不是由你引起的-这样它们就不会丢失:
func handler(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
switch r {
case nil: // 没有panic
// (2) 在这里...
case http.ErrAbortHandler:
log.Println("Recovered in handler")
// (2) 或者在这里...
default:
panic(r) // 重新引发任何意外的panic
}
}()
checkSomeThing(w, r) // (1) 在这里发生panic...
// (3) ... 这部分永远不会运行
}
在上面的panic恢复代码中,http.ServeHTTP
将永远不会知道发生了panic-并自然地刷新请求缓冲区。
英文:
Always try to handle errors at the API level - bubbling errors up the call-stack. There are, however, cases where this is not possible - maybe your handler is one of many in a middleware chain of handlers, and you don't want those other layers to complete. So...
If you want to immediately abort a request from your handler, the standard library supports panic
& recovery.
From the http.Handler docs:
> 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.
>
and 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.
so to do this in your handler:
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)
}
There is a catch...
Writes to client will be buffered - and panic
ing will likely cause these buffered writes to be lost.
To forcibly flush the buffer:
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
panic(http.ErrAbortHandler)
or you can add your own panic
recovery. Just ensure, in the recovery, to "re-raise" any panic
s - if they were not cause by you - so they won't be lost:
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
}
in the above panic-recovery code, http.ServeHTTP
will never know there was a panic - and flush the request buffers naturally.
答案2
得分: 1
根据http.Error文档的说明:
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, "操作完成!", http.StatusOK)
fmt.Println("处理程序结束。")
return
}
func checkSomeThing(w http.ResponseWriter, r *http.Request) error{
http.Error(w, "错误的请求!", http.StatusBadRequest)
return errors.New("错误的请求")
}
英文:
According to the http.Error documentation
> 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.
So after executing the checkSomeThing
function it just writing the error string in responsewriter
and continuing processing further operations.
A working version of your code is given below:
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")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论