英文:
Idiomatic way to handle template errors in golang
问题
假设我有一个如下所示的html/template
:
<html>
<body>
<p>{{SomeFunc .SomeData}}</p>
</body>
有时候SomeFunc
会返回一个错误。有没有一种惯用的方法来处理这个错误?
如果我直接写入ResponseWriter
,那么在遇到错误之前,状态码200已经被写入了。
var tmpl *template.Template
func Handler(w http.ResponseWriter, r *http.Request) {
err := tmpl.Execute(w, data)
// "<html><body><p>"已经被写入...
// 怎么处理err?
}
最好的情况是我想返回一个状态码400或类似的东西,但是如果我直接在ResponseWriter
上使用template.Execute
,我看不到有什么办法可以做到这一点。我是否漏掉了什么?
英文:
Say I have a html/template
like the following:
<html>
<body>
<p>{{SomeFunc .SomeData}}</p>
</body>
and sometimes SomeFunc
returns an error. Is there an idiomatic way to deal with this?
If I write directly to the ResponseWriter
, then a status code 200 has already been written before I encounter the error.
var tmpl *template.Template
func Handler(w http.ResponseWriter, r *http.Request) {
err := tmpl.Execute(w, data)
// "<html><body><p>" has already been written...
// what to do with err?
}
Preferably I would return a status code 400 or some such, but I can't see a way to do this if I use template.Execute
directly on the ResponseWriter
. Is there something I'm missing?
答案1
得分: 16
由于模板引擎是即时生成输出的,SomeFunc
调用之前的模板部分已经发送到输出中。如果输出没有缓冲,它们(以及 HTTP 200 状态)可能已经被发送。
你无法对此做任何处理。
你可以在调用 template.Execute()
之前执行检查。在简单的情况下,调用 SomeFunc()
并检查其返回值应该就足够了。如果你选择这种方法,并且 SomeFunc()
的返回值很复杂,你不必在模板中再次调用它,你可以将其返回值直接传递给传递给模板的参数,并在模板中引用这个值(这样 SomeFunc()
就不需要执行两次)。
如果这还不够或者你无法控制它,你可以创建一个 bytes.Buffer
或 strings.Builder
,将模板直接执行到这个缓冲区中,在 Execute()
返回后,检查是否有错误。如果有错误,发送适当的错误消息/页面。如果一切正常,你可以将缓冲区的内容发送到 ResponseWriter
。
代码示例如下:
buf := &bytes.Buffer{}
err := tmpl.Execute(buf, data)
if err != nil {
// 发送错误消息,例如:
http.Error(w, "嘿,请求有问题!", http.StatusBadRequest) // HTTP 400 状态
} else {
// 没有错误,发送内容,隐含 HTTP 200 响应状态
buf.WriteTo(w)
}
英文:
Since the template engine generates the output on-the-fly, parts of the template preceding the SomeFunc
call are already sent to the output. And if the output is not buffered, they (along with the HTTP 200 status) may already be sent.
You can't do anything about that.
What you can do is perform the check before you call template.Execute()
. In trivial case it should be enough to call SomeFunc()
and check its return value. If you choose this path and the return value of SomeFunc()
is complex, you do not have to call it again from the template, you can simply pass its return value to the params you pass to the template and refer to this value in the template (so SomeFunc()
won't have to be executed twice).
If this is not enough or you can't control it, you can create a bytes.Buffer
or strings.Builder
, execute your template directed into this buffer, and after the Execute()
returns, check if there were errors. If there were errors, send back a proper error message / page. If everything went ok, you can just send the content of the buffer to the ResponseWriter
.
This could look something like this:
buf := &bytes.Buffer{}
err := tmpl.Execute(buf, data)
if err != nil {
// Send back error message, for example:
http.Error(w, "Hey, Request was bad!", http.StatusBadRequest) // HTTP 400 status
} else {
// No error, send the content, HTTP 200 response status implied
buf.WriteTo(w)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论