英文:
Passing data between http handler functions in GO
问题
我有一个HTTP处理函数,只有在电子邮件尚未被占用时,才将其保存到数据库中。如果电子邮件已经被占用或其他数据无效,我希望重定向回表单,并通知用户错误发生的位置。
func validate(w http.ResponseWriter, r *http.Request) {
//查询数据库以检查r.FormValues的数据是否有效
//如果表单值无效,则重定向回注册表单,并传递指示表单数据无效的数据
{
http.Redirect(w, r, "/register", http.StatusFound)
}
}
我该如何从validate()
函数向register()
函数发送数据?是否可以在r* http.Request
中添加某种结构,以便在调用http.Redirect()
时将其传递给register()
函数?
英文:
I have a http handler function which saves an email to a database only if that email is not already taken. If the email has already been taken or some other data is invalid I want to redirect back to the form and notify the user where the errors occured.
func validate(w http.ResponseWriter, r *http.Request) {
//query database to check if data r.FormValues were
//valid
//IF form values were not valid redirect back to the
//registration form and pass on data indicating the
//form data was not valid
{
http.Redirect(w, r, "/register", http.StatusFound)
}
}
How on earth do I send data from func validate() to func register()? Is it possible to add some sort of struct to r* http.Request so it gets passed to func register() when http.Redirect() is called?
答案1
得分: 4
在你的示例中,如果你的表单提交指向validate()
处理程序,该处理程序将返回一个HTTP响应(重定向),浏览器将再次调用/register
。你的validate()
处理程序和register()
处理程序之间没有连接,因为浏览器会发起另一个HTTP请求,所以会创建另一个*http.Request
值并在第二次调用时传递。
你可以在重定向URL中指定参数,例如重定向到/register?someParam=someValue
,但这只是一个不必要的往返,并且会使事情变得复杂。
一个更简单的解决方案是不要在处理程序级别上分离表单渲染和验证。同一个处理程序可以同时处理两者,因此不需要在2个处理程序之间共享数据。
示例:
func register(w http.ResponseWriter, r *http.Request) {
// 用于渲染页面的参数
m := map[string]interface{}{}
// 表单是否已提交?
if r.FormValue("submitRegister") != "" {
// 检查提交的值
// 例如,检查电子邮件,假设它已经在使用中:
email := r.FormValue("Email")
if alreadyInUse {
m["Error"] = "电子邮件已经在使用中!"
}
if m["Error"] == "" {
// 如果所有值都正确,创建用户并重定向:
http.Redirect(w, r, "/home", http.StatusFound)
return // 并返回!
}
// 将提交的值存储在参数中,这样当我们再次渲染
// 注册表单时,我们将提交的参数填充为初始值,
// 这样用户不需要再次填写所有内容(除了密码可能)
m["Email"] = email
}
// 无论是没有提交还是验证错误
// 使用提交的值作为初始值渲染注册表单
// 如果m["Error"]已设置,则在表单上方渲染错误
registerTempl.Execute(w, m)
}
当然,你可以将其拆分为函数,并且可以有一个单独的validate()
函数,但是表单提交仍然必须指向与注册页面相同的路径(例如/register
):
func register(w http.ResponseWriter, r *http.Request) {
// 用于渲染页面的参数
m := map[string]interface{}{}
// 表单是否已提交?
if r.FormValue("submitRegister") != "" {
validate(w, r, m)
if m["Error"] == "" {
return // 并返回!
}
}
// 无论是没有提交还是验证错误
// 使用提交的值作为初始值渲染注册表单
// 如果m["Error"]已设置,则在表单上方渲染错误
registerTempl.Execute(w, m)
}
func validate(w http.ResponseWriter, r *http.Request, m map[string]interface{}) {
// 检查提交的值
// 例如,检查电子邮件,假设它已经在使用中:
email := r.FormValue("Email")
if alreadyInUse {
m["Error"] = "电子邮件已经在使用中!"
}
if m["Error"] == "" {
// 如果所有值都正确,创建用户并重定向:
http.Redirect(w, r, "/home", http.StatusFound)
return
}
// 将提交的值存储在参数中,这样当我们
// 再次渲染注册表单时,我们将提交的参数填充为初始值,
// 这样用户不需要再次填写所有内容(除了密码可能)
m["Email"] = email
}
英文:
In your example if your form submit is directed to the validate()
handler which will send back an HTTP response (which will be a redirect), the browser will make another call to /register
. There is no connection between your validate()
handler and your register()
handler, the *http.Request
value will not be the same as the browser makes another HTTP request and so another *http.Request
value will be created and passed on the second call.
You could specify parameters in the redirect URL, e.g. redirect to /register?someParam=someValue
but that is just an unnecessary round-trip and complicates things.
An easier solution is to not separate form render and validation (on the handler level). The same handler can take care of both, so no data sharing is required between 2 handlers.
Example:
func register(w http.ResponseWriter, r *http.Request) {
// Params for rendering the page
m := map[string]interface{}{}
// Is form submitted?
if r.FormValue("submitRegister") != "" {
// check submitted values
// E.g. check email, let's say it's already in use:
email := r.FormValue("Email")
if alreadyInUse {
m["Error"] = "Email already in use!"
}
if m["Error"] == "" {
// If all values are OK, create user, and redirect:
http.Redirect(w, r, "/home", http.StatusFound)
return // AND return!
}
// Store submitted values in params, so when we render
// the registration form again, we fill submitted params as initial values,
// so user don't have to fill everything again (expect maybe the password)
m["Email"] = email
}
// Either no submit or validation errors
// Render the registration form, using submitted values as initial ones
// Also if m["Error"] is set, render the error above the form
registerTempl.Execute(w, m)
}
Of course you can break it down to functions, and you can have a separate validate()
function, but still the form submit have to point to the same path as your register page (e.g. /register
):
func register(w http.ResponseWriter, r *http.Request) {
// Params for rendering the page
m := map[string]interface{}{}
// Is form submitted?
if r.FormValue("submitRegister") != "" {
validate(w, r, m)
if m["Error"] == "" {
return // AND return!
}
}
// Either no submit or validation errors
// Render the registration form, using submitted values as initial ones
// Also if m["Error"] is set, render the error above the form
registerTempl.Execute(w, m)
}
func validate(w http.ResponseWriter, r *http.Request, m map[string]interface{}) {
// check submitted values
// E.g. check email, let's say it's already in use:
email := r.FormValue("Email")
if alreadyInUse {
m["Error"] = "Email already in use!"
}
if m["Error"] == "" {
// If all values are OK, create user, and redirect:
http.Redirect(w, r, "/home", http.StatusFound)
return
}
// Store submitted values in params, so when we
// render the registration form again, we fill submitted params as initial values,
// so user don't have to fill everything again (expect maybe the password)
m["Email"] = email
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论