// Chapter 35 example: notes in a slice, protected by a mutex. The notes are gone
// when the server stops.
// Run with: go run exercises/35/02-notes-in-memory.go
package main
import (
"bytes"
"fmt"
"html/template"
"net/http"
"slices"
"strings"
"sync"
"unicode/utf8"
)
const maxNoteLength = 200
type note struct {
Text string
}
// mu protects notes. Any code that reads or changes notes must hold mu.
var mu sync.Mutex
var notes = []note{}
// allNotes returns a copy, so that the caller does not need mu to read it.
func allNotes() []note {
mu.Lock()
defer mu.Unlock()
return slices.Clone(notes)
}
func addNote(n note) {
mu.Lock()
defer mu.Unlock()
notes = append(notes, n)
}
const notesText = `
Notes
Notes
{{range .}}
- {{.Text}}
{{else}}
- No notes yet.
{{end}}
`
var notesTemplate = template.Must(template.New("notes").Parse(notesText))
func handleHome(w http.ResponseWriter, r *http.Request) {
var page bytes.Buffer
err := notesTemplate.Execute(&page, allNotes())
if err != nil {
fmt.Println("Template error:", err)
http.Error(w, "The page could not be built.", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
page.WriteTo(w)
}
func handleAdd(w http.ResponseWriter, r *http.Request) {
text := strings.TrimSpace(r.FormValue("text"))
if text == "" {
http.Error(w, "The note is empty.", http.StatusBadRequest)
return
}
if utf8.RuneCountInString(text) > maxNoteLength {
http.Error(w, "The note is too long.", http.StatusBadRequest)
return
}
addNote(note{Text: text})
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func main() {
http.HandleFunc("GET /{$}", handleHome)
http.HandleFunc("POST /notes", handleAdd)
fmt.Println("Server running at http://localhost:8080")
fmt.Println("Press Ctrl+C to stop it.")
err := http.ListenAndServe("localhost:8080", nil)
if err != nil {
fmt.Println("Server stopped:", err)
}
}