Form Validation and Processing in Go

1st November 2013

In this post I'm going to run through a start-to-finish tutorial for building a online contact form in Go. I'll be trying to explain the different steps in detail, as well as outlining a sensible pattern for processing that can be extended to other forms.

Let's begin the example by creating a new directory for the application, along with a main.go file for our code and a couple of vanilla HTML templates:

$ mkdir -p contact-form/templates
$ cd contact-form
$ touch main.go templates/index.html templates/confirmation.html
File: templates/index.html
<h1>Contact</h1>
<form action="/" method="POST" novalidate>
  <div>
    <label>Your email:</label>
    <input type="email" name="email">
  </div>
  <div>
    <label>Your message:</label>
    <textarea name="content"></textarea>
  </div>
  <div>
    <input type="submit" value="Send message">
  </div>
</form>
File: templates/confirmation.html
<h1>Confirmation</h1>
<p>Your message has been sent!</p>

Our contact form will issue a POST request to /, which will be the same URL path that we use for presenting the form. This means that we'll need to route requests for the same URL to different handlers based on the HTTP method.

There are a few ways of achieving this, but we'll use Pat – a third-party routing library which I've talked about before. You'll need to install it if you're following along:

$ go get github.com/bmizerany/pat

Go ahead and create a skeleton for the application:

File: main.go
package main

import (
  "github.com/bmizerany/pat"
  "html/template"
  "log"
  "net/http"
)

func main() {
  mux := pat.New()
  mux.Get("/", http.HandlerFunc(index))
  mux.Post("/", http.HandlerFunc(send))
  mux.Get("/confirmation", http.HandlerFunc(confirmation))

  log.Println("Listening...")
  http.ListenAndServe(":3000", mux)
}

func index(w http.ResponseWriter, r *http.Request) {
  render(w, "templates/index.html", nil)
}

func send(w http.ResponseWriter, r *http.Request) {
  // Validate form
  // Send message in an email
  // Redirect to confirmation page
}

func confirmation(w http.ResponseWriter, r *http.Request) {
  render(w, "templates/confirmation.html", nil)
}

func render(w http.ResponseWriter, filename string, data interface{}) {
  tmpl, err := template.ParseFiles(filename)
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  if err := tmpl.Execute(w, data); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

This is fairly straightforward stuff so far. The only real point of note is that we've put the template handling into a render function to cut down on boilerplate code.

If you run the application:

$ go run main.go
Listening...

And visit localhost:3000 in your browser you should see the contact form, although it doesn't do anything yet.

Now for the interesting part. Let's add a couple of validation rules to our contact form, display the errors if there are any, and make sure that the form values get presented back if there's an error so the user doesn't need to retype them.

One approach to setting this up is to add the code inline in our send handler, but personally I find it cleaner and neater to break out the logic into a separate message.go file:

$ touch message.go
File: message.go
package main

import (
  "regexp"
  "strings"
)

type Message struct {
  Email    string
  Content string
  Errors  map[string]string
}

func (msg *Message) Validate() bool {
  msg.Errors = make(map[string]string)

  re := regexp.MustCompile(".+@.+\\..+")
  matched := re.Match([]byte(msg.Email))
  if matched == false {
    msg.Errors["Email"] = "Please enter a valid email address"
  }

  if strings.TrimSpace(msg.Content) == "" {
    msg.Errors["Content"] = "Please write a message"
  }

  return len(msg.Errors) == 0
}

So what's going on here?

We've started by defining a new Message type, consisting of the Email and Content values (which will hold the data from the submitted form), along with an Errors map to hold any validation error messages.

We then created a Validate method that acts on a given Message, which summarily checks the format of the email address and makes sure that the content isn't blank. In the event of any errors we add them to the Errors map, and finally return a true or false value to indicate whether validation passed successful or not.

This approach means that we can keep the code in our send handler fantastically light. All we need it to do is retrieve the form values from the POST request, create a new Message object with them, and call Validate(). If the validation fails we'll then want to reshow the contact form, passing back the relevant Message object.

File: main.go
...
func send(w http.ResponseWriter, r *http.Request) {
  msg := &Message{
    Email: r.FormValue("email"),
    Content: r.FormValue("content"),
  }

  if msg.Validate() == false {
    render(w, "templates/index.html", msg)
    return
  }

  // Send message in an email
  // Redirect to confirmation page
}
...

As a side note, in this example above we're using the FormValue method on the request to access the POST data. We could also access the data directly via r.Form, but there is a gotcha to point out – by default r.Form will be empty until it is filled by calling ParseForm on the request. Once that's done, we can access it in the same way as any url.Values type. For example:

err := r.ParseForm()
// Handle error
msg := &Message{
  Email: r.Form.Get("email"),
  Content: r.Form.Get("content"),
}

Anyway, let's update our template so it shows the validation errors (if they exist) above the relevant fields, and repopulate the form inputs with any information that the user previously typed in:

File: templates/index.html
<style type="text/css">.error {color: red;}</style>

<h1>Contact</h1>
<form action="/" method="POST" novalidate>
  <div>
    {{ with .Errors.Email }}
    <p class="error">{{ . }}</p>
    {{ end }}
    <label>Your email:</label>
    <input type="email" name="email" value="{{ .Email }}">
  </div>
  <div>
    {{ with .Errors.Content }}
    <p class="error" >{{ . }}</p>
    {{ end }}
    <label>Your message:</label>
    <textarea name="content">{{ .Content }}</textarea>
  </div>
  <div>
    <input type="submit" value="Send message">
  </div>
</form>

Go ahead and give it a try:

$ go run main.go message.go
Listening...

Still, our contact form is pretty useless unless we actually do something with it. Let's add a Deliver method which sends the contact form message to a particular email address. In the code below I'm using Gmail, but the same thing should work with any other SMTP server.

File: message.go
package main

import (
  "fmt"
  "net/smtp"
  "regexp"
  "strings"
)
...

func (msg *Message) Deliver() error {
  to := []string{"someone@example.com"}
  body := fmt.Sprintf("Reply-To: %v\r\nSubject: New Message\r\n%v", msg.Email, msg.Content)

  username := "you@gmail.com"
  password := "..."
  auth := smtp.PlainAuth("", username, password, "smtp.gmail.com")

  return smtp.SendMail("smtp.gmail.com:587", auth, msg.Email, to, []byte(body))
}

The final step is to head back to our main.go file, add some code to call Deliver(), and issue a 303 redirect to the confirmation page that we made earlier:

File: main.go
...
func send(w http.ResponseWriter, r *http.Request) {
  msg := &Message{
    Email: r.FormValue("email"),
    Content: r.FormValue("content"),
  }

  if msg.Validate() == false {
    render(w, "templates/index.html", msg)
    return
  }

  if err := msg.Deliver(); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }
  http.Redirect(w, r, "/confirmation", http.StatusSeeOther)
}
...

Additional Tools

If mapping form data to objects is something that you're doing a lot of, you may find Gorilla Schema's automatic decoder useful. If we were using it for our contact form example, the code would look a bit like this:

import "github.com/gorilla/schema"
...
err := r.ParseForm()
// Handle error
msg := new(Message)
decoder := schema.NewDecoder()
decoder.Decode(msg, r.Form)

Additionally, Goforms appears to be a promising idea, with a fairly slick Django-like approach to dealing with forms. However, the existing validation options are fairly limited and the library doesn't seem to be under active development at the moment. It's still worth a look though, especially if you're thinking of rolling something a bit more generic for your form handling.

If you found this post useful, you might like to subscribe to my RSS feed.

Filed under: golang, tutorial

‚Äč