final-noclosure.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "errors"
  7. "html/template"
  8. "io/ioutil"
  9. "log"
  10. "net/http"
  11. "regexp"
  12. )
  13. type Page struct {
  14. Title string
  15. Body []byte
  16. }
  17. func (p *Page) save() error {
  18. filename := p.Title + ".txt"
  19. return ioutil.WriteFile(filename, p.Body, 0600)
  20. }
  21. func loadPage(title string) (*Page, error) {
  22. filename := title + ".txt"
  23. body, err := ioutil.ReadFile(filename)
  24. if err != nil {
  25. return nil, err
  26. }
  27. return &Page{Title: title, Body: body}, nil
  28. }
  29. func viewHandler(w http.ResponseWriter, r *http.Request) {
  30. title, err := getTitle(w, r)
  31. if err != nil {
  32. return
  33. }
  34. p, err := loadPage(title)
  35. if err != nil {
  36. http.Redirect(w, r, "/edit/"+title, http.StatusFound)
  37. return
  38. }
  39. renderTemplate(w, "view", p)
  40. }
  41. func editHandler(w http.ResponseWriter, r *http.Request) {
  42. title, err := getTitle(w, r)
  43. if err != nil {
  44. return
  45. }
  46. p, err := loadPage(title)
  47. if err != nil {
  48. p = &Page{Title: title}
  49. }
  50. renderTemplate(w, "edit", p)
  51. }
  52. func saveHandler(w http.ResponseWriter, r *http.Request) {
  53. title, err := getTitle(w, r)
  54. if err != nil {
  55. return
  56. }
  57. body := r.FormValue("body")
  58. p := &Page{Title: title, Body: []byte(body)}
  59. err = p.save()
  60. if err != nil {
  61. http.Error(w, err.Error(), http.StatusInternalServerError)
  62. return
  63. }
  64. http.Redirect(w, r, "/view/"+title, http.StatusFound)
  65. }
  66. func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
  67. t, err := template.ParseFiles(tmpl + ".html")
  68. if err != nil {
  69. http.Error(w, err.Error(), http.StatusInternalServerError)
  70. return
  71. }
  72. err = t.Execute(w, p)
  73. if err != nil {
  74. http.Error(w, err.Error(), http.StatusInternalServerError)
  75. }
  76. }
  77. var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")
  78. func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
  79. m := validPath.FindStringSubmatch(r.URL.Path)
  80. if m == nil {
  81. http.NotFound(w, r)
  82. return "", errors.New("Invalid Page Title")
  83. }
  84. return m[2], nil // The title is the second subexpression.
  85. }
  86. func main() {
  87. http.HandleFunc("/view/", viewHandler)
  88. http.HandleFunc("/edit/", editHandler)
  89. http.HandleFunc("/save/", saveHandler)
  90. log.Fatal(http.ListenAndServe(":8080", nil))
  91. }