Source file doc/articles/wiki/final.go

     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  
     5  //go:build ignore
     6  
     7  package main
     8  
     9  import (
    10  	"html/template"
    11  	"log"
    12  	"net/http"
    13  	"os"
    14  	"regexp"
    15  )
    16  
    17  type Page struct {
    18  	Title string
    19  	Body  []byte
    20  }
    21  
    22  func (p *Page) save() error {
    23  	filename := p.Title + ".txt"
    24  	return os.WriteFile(filename, p.Body, 0600)
    25  }
    26  
    27  func loadPage(title string) (*Page, error) {
    28  	filename := title + ".txt"
    29  	body, err := os.ReadFile(filename)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	return &Page{Title: title, Body: body}, nil
    34  }
    35  
    36  func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
    37  	p, err := loadPage(title)
    38  	if err != nil {
    39  		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
    40  		return
    41  	}
    42  	renderTemplate(w, "view", p)
    43  }
    44  
    45  func editHandler(w http.ResponseWriter, r *http.Request, title string) {
    46  	p, err := loadPage(title)
    47  	if err != nil {
    48  		p = &Page{Title: title}
    49  	}
    50  	renderTemplate(w, "edit", p)
    51  }
    52  
    53  func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
    54  	body := r.FormValue("body")
    55  	p := &Page{Title: title, Body: []byte(body)}
    56  	err := p.save()
    57  	if err != nil {
    58  		http.Error(w, err.Error(), http.StatusInternalServerError)
    59  		return
    60  	}
    61  	http.Redirect(w, r, "/view/"+title, http.StatusFound)
    62  }
    63  
    64  var templates = template.Must(template.ParseFiles("edit.html", "view.html"))
    65  
    66  func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
    67  	err := templates.ExecuteTemplate(w, tmpl+".html", p)
    68  	if err != nil {
    69  		http.Error(w, err.Error(), http.StatusInternalServerError)
    70  	}
    71  }
    72  
    73  var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")
    74  
    75  func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
    76  	return func(w http.ResponseWriter, r *http.Request) {
    77  		m := validPath.FindStringSubmatch(r.URL.Path)
    78  		if m == nil {
    79  			http.NotFound(w, r)
    80  			return
    81  		}
    82  		fn(w, r, m[2])
    83  	}
    84  }
    85  
    86  func main() {
    87  	http.HandleFunc("/view/", makeHandler(viewHandler))
    88  	http.HandleFunc("/edit/", makeHandler(editHandler))
    89  	http.HandleFunc("/save/", makeHandler(saveHandler))
    90  
    91  	log.Fatal(http.ListenAndServe(":8080", nil))
    92  }
    93  

View as plain text