// 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

` 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) } }