Reputation: 51
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?
Upvotes: 4
Views: 3779
Reputation: 417462
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
}
Upvotes: 5