Go 在handlefunc之外安全地终止请求

Go 在handlefunc之外安全地终止请求,go,Go,在没有显式返回handlefunc的情况下,如何在调度器(handlefunc)之外安全地终止请求?在下面的代码中,我最终在响应中看到“坏请求”和“未找到”,但我希望f()中止请求——这样我就不必用错误检查和返回来扰乱dispatcher package main import ( "net/http" ) func f(w http.ResponseWriter, r *http.Request, k string) string{ if v, ok := r.Form[k

在没有显式返回handlefunc的情况下,如何在调度器(handlefunc)之外安全地终止请求?在下面的代码中,我最终在响应中看到“坏请求”和“未找到”,但我希望f()中止请求——这样我就不必用错误检查和返回来扰乱dispatcher

package main

import (
    "net/http"
)

func f(w http.ResponseWriter, r *http.Request, k string) string{
    if v, ok := r.Form[k]; !ok{     
        http.Error(w, "Bad request", http.StatusBadRequest)
        return ""
    }else{
        return v[0]
    }
}

func main(){
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
        foo := f(w, r, "foo") //make sure foo is passed, if not abort with "bad request" without requiring an explicit return here
        bar := f(w, r, "bar") //make sure bar is passed, if not abort with "bad request" without requiring an explicit return here
        //lookup in db...
        //abort with a 404 if not found in db
        http.NotFound(w, r)         
    })  

    http.ListenAndServe(":6000", nil)
}
以上是重构以下内容的尝试:

func main(){
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
        if foo,ok:=r.Form["foo"];!ok{
            http.Error(w, "Bad request", http.StatusBadRequest)
            return
        }
        if bar,ok:=r.Form["bar"];!ok{
            http.Error(w, "Bad request", http.StatusBadRequest)
            return
        }
        //lookup in db...
        //abort with a 404 if not found in db
        http.NotFound(w, r)         
    })  

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

我不认为在函数中尝试中止是您想要做的事情。相反,要减少验证的重复性。您可以编写类似于带有
func Require(form url.Values,required…string)错误的
package validate
,并且在处理程序中,如果err:=validate.Require(r.form,“foo”,“bar”);呃!=无{…}

或者您可以做一个更通用的验证器:可能将字段名的
映射到验证类型(数字、字符串等),如果没有错误,则返回另一个
映射,即
nil
,否则返回
{“fieldName”:error}

其中一位写道。在中还有另一个基于结构的验证实现。(我都没试过。)Redditor提出了一个棘手的问题,即当你的应用程序变得更加复杂时,在得到一个“框架”之前,你可以抽象多少验证,而我没有一个简单的答案


在某些情况下,确实发生了意想不到的事情,服务器除了给用户一个不透明的“500 Internal server Error”响应之外,再也做不到更好的事情了,但是问题在您的
HandlerFunc
的下面深深地显现出来。典型的例子是一个编程错误,比如零指针解引用。是在“错误无法恢复”时使用
panic
。(类似的工具可以帮助记录
panic
s等)

不必要的
panic
s是跛脚的,因为:

  • panic
    s使完全忘记必要的错误处理变得更容易,因为这意味着事情可以
    panic
    ,但它们可以
    err
  • 死机
    /
    恢复
    错误处理代码丑陋且难以维护
  • panic
    s引入与标准
    err
    返回不一致
  • panic
    s使用堆栈展开,这比正常控制流慢得多
我做了一个快速的
grep
,基本上所有对标准库和其他官方存储库中的
panic
的调用都是为了捕捉编程/内部错误,而不是在意外的外部条件(如输入错误或I/O错误)下崩溃。像找不到文件/对象或无效用户输入这样的事情通常可以比通过请求崩溃(至少可能返回特定错误)更优雅地处理,而且它们并不是真的出乎意料(这是互联网;您将获得无效输入和对不存在的东西的请求)。因此,对于正常请求中止而言,
panic
而不是,它用于在“不可能发生”的情况发生且请求无法继续时崩溃



同样,在这里,我会在handler函数中使用标准的
if
块来处理无效的输入,但要重新安排验证,这样每个必需的参数就不需要太多的代码。

你说的“终止请求”是什么意思?为什么您不想在处理程序中使用显式的
return
?@Ainar-G编辑以显示我试图避免的代码重复。在正常意义上终止。返回将导致大量混乱,特别是如果我检查的查询字符串参数超过1个。如果没有显式的
返回
,我认为这是不可能的。如果bar=checkForm(r,w,“bar”);bar==“{return}
。我还是保留原样,因为它更明确。
返回
有什么不好?Go依赖于显式错误处理。试着像python或ruby那样编写这篇文章只会让你的生活变得艰难。我甚至不会在这里建议
panic
<代码>死机
通常用于指示程序员错误或当您希望程序因堆栈跟踪而崩溃时。除非您编写自己的恢复,否则它也只会为您清除一个InternalServerError。通过正常的错误处理,有更好的方法可以做到这一点;我刚才编辑了一下,想让它更清楚。但这里有一个与示例用例无关的一般性问题(“如何在
HandlerFunc
?”之外中止”),因此,我不是建议您不能中止,而是说您不应该中止验证,并给出原因和替代方案。(扩展和加强了“不要惊慌”,并在“如何验证”中添加了一些内容)也一样。)