Untuk keperluan modul ini, kita akan tambahkan form sederhana yang sudah disediakan di bootstrap pada file templates/home.page.gohtml. Berikut code yang digunakan:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
<title>Home</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col">
<h1 class="mt-3">Home Page</h1>
<hr>
<form action="/login" method="post">
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<hr>
<small>Your request came from {{.IP}}</small>
</div>
</div>
</div>
</body>
</html>
Jika kita jalankan webapp dan buka di web browser, kurang lebih akan tampil seperti berikut:

Buka file cmd/web/handlers.go, kita tambahkan fungsi untuk handling form submit diatas.
package main
import (
"fmt"
"html/template"
"log"
"net/http"
"path"
)
var templatePath = "./templates/"
func (app *application) Home(w http.ResponseWriter, r *http.Request) {
_ = app.render(w, r, "home.page.gohtml", &TemplateData{})
}
type TemplateData struct {
IP string
Data map[string]any
}
func (app *application) render(w http.ResponseWriter, r *http.Request, t string, data *TemplateData) error {
//parse template
parsedTemplate, err := template.ParseFiles(path.Join(templatePath, t))
if err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return err
}
//gunakan middleware yang telah kita buat.
data.IP = app.ipFromContext(r.Context())
//execute template
err = parsedTemplate.Execute(w, data)
if err != nil {
return err
}
return nil
}
func (app *application) Login(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Println(err)
http.Error(w, "bad request", http.StatusBadRequest)
return
}
email := r.Form.Get("email")
password := r.Form.Get("password")
log.Println(email, password)
fmt.Fprint(w, email)
}
Kemudian buka file cmd/web/routes.go, tambahkan route untuk /login
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func (app *application) routes() http.Handler {
mux := chi.NewRouter()
//register middleware
mux.Use(middleware.Recoverer)
mux.Use(app.addIPToContext) //tambahkan middleware yang kita buat
//register routes
mux.Get("/", app.Home)
mux.Post("/login", app.Login)
//static assets
fileServer := http.FileServer(http.Dir("./static"))
mux.Handle("/static/*", http.StripPrefix("/static/", fileServer))
return mux
}
Selanjutnya, kita buat file cmd/web/forms.go, yang akan berisi validasi untuk form yang disubmit.
package main
import (
"net/url"
"strings"
)
type errors map[string][]string
func (e errors) Get(field string) string {
errorSlice := e[field]
if len(errorSlice) == 0 {
return ""
}
return errorSlice[0]
}
func (e errors) Add(field, message string) {
e[field] = append(e[field], message)
}
type Form struct {
Data url.Values
Errors errors
}
func NewForm(data url.Values) *Form {
return &Form{
Data: data,
Errors: map[string][]string{},
}
}
func (f *Form) Has(field string) bool {
x := f.Data.Get(field)
return x != ""
// if x == "" {
// return false
// }
// return true
}
func (f *Form) Required(fields ...string) {
for _, field := range fields {
value := f.Data.Get(field)
if strings.TrimSpace(value) == "" {
f.Errors.Add(field, "This field cannot be blank")
}
}
}
func (f *Form) Check(ok bool, key, message string) {
if !ok {
f.Errors.Add(key, message)
}
}
func (f *Form) Valid() bool {
return len(f.Errors) == 0
}
Setelah kode validasi dibuat, kita implementasikan pada file cmd/web/handlers.go.
package main
import (
"fmt"
"html/template"
"log"
"net/http"
"path"
)
var templatePath = "./templates/"
func (app *application) Home(w http.ResponseWriter, r *http.Request) {
_ = app.render(w, r, "home.page.gohtml", &TemplateData{})
}
type TemplateData struct {
IP string
Data map[string]any
}
func (app *application) render(w http.ResponseWriter, r *http.Request, t string, data *TemplateData) error {
//parse template
parsedTemplate, err := template.ParseFiles(path.Join(templatePath, t))
if err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return err
}
//gunakan middleware yang telah kita buat.
data.IP = app.ipFromContext(r.Context())
//execute template
err = parsedTemplate.Execute(w, data)
if err != nil {
return err
}
return nil
}
func (app *application) Login(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Println(err)
http.Error(w, "bad request", http.StatusBadRequest)
return
}
//validation goes here
form := NewForm(r.PostForm)
form.Required("email", "password")
if !form.Valid() {
fmt.Fprint(w, "Invalid form")
return
}
email := r.Form.Get("email")
password := r.Form.Get("password")
log.Println(email, password)
fmt.Fprint(w, email)
}
Sampai disini, persiapan Webapp Testing Form telah selesai. Pada modul selanjutnya kita akan membuat fungsi untuk test validasi form diatas.