用 Go 创建网站

    这个 kvWeb.go 和之前的 www.go 的第一个不同是,kvWeb.go 使用 http.NewServeMux 来处理 HTTP 请求,因为对于稍复杂点的 web 应用来说,它有更多的功能。

    kvWeb.go 的第一部分如下:

    第八章(Go UNIX系统编程)中,您已经在 kvSaveLoad.go 见过上面的代码了。

    kvWeb.go 的第二部分代码如下:

    1. func save() error {
    2. fmt.Println("Saving", DATAFILE)
    3. err := os.Remove(DATAFILE)
    4. if err != nil {
    5. fmt.Println(err)
    6. }
    7. saveTo, err := os.Create(DATAFILE)
    8. if err != nil {
    9. fmt.Println("Cannot create", DATAFILE)
    10. return err
    11. }
    12. defer saveTo.Close()
    13. encoder := gob.NewEncoder(saveTo)
    14. err = encoder.Encode(DATA)
    15. if err != nil {
    16. fmt.Println("Cannot save to", DATAFILE)
    17. return err
    18. }
    19. return nil
    20. }
    21. func load() error {
    22. fmt.Println("Loading", DATAFILE)
    23. loadFrom, err := os.Open(DATAFILE)
    24. defer loadFrom.Close()
    25. if err != nil {
    26. fmt.Println("Empty key/value store!")
    27. return err
    28. }
    29. decoder := gob.NewDecoder(loadFrom)
    30. decoder.Decode(&DATA)
    31. return nil
    32. }
    33. func ADD(k string, n myElement) bool {
    34. if k == "" {
    35. return false
    36. }
    37. if LOOKUP(k) == nil {
    38. DATA[k] = n
    39. return true
    40. }
    41. return false
    42. }
    43. func DELETE(k string) bool {
    44. if LOOKUP(k) != nil {
    45. delete(DATA, k)
    46. return true
    47. }
    48. return false
    49. }
    50. func LOOKUP(k string) *myElement {
    51. _, ok := DATA[k]
    52. if ok {
    53. n := DATA[k]
    54. return &n
    55. } else {
    56. return nil
    57. }
    58. }
    59. func CHANGE(k string, n myElement) bool {
    60. DATA[k] = n
    61. return true
    62. func PRINT() {
    63. for k, d := range DATA {
    64. }
    65. }

    您应该也熟悉上面这段代码,因为它曾第一次出现在的 kvSaveLoad.go 中。

    kvWeb.go 的第三部分如下:

    1. func homePage(w http.ResponseWriter, r *http.Request) {
    2. fmt.Println("Serving", r.Host, "for", r.URL.Path)
    3. myT := template.Must(template.ParseGlob("home.gohtml"))
    4. myT.ExecuteTemplate(w, "home.gohtml", nil)
    5. }
    6. func listAll(w http.ResponseWriter, r *http.Request) {
    7. fmt.Println("Listing the contents of the KV store!")
    8. fmt.Fprintf(w, "<a href=\"/\" style=\"margin-right: 20px;\">Home sweet home!</a>")
    9. fmt.Fprintf(w, "<a href=\"/list\" style=\"margin-right: 20px;\">List all elements!</a>")
    10. fmt.Fprintf(w, "<a href=\"/change\" style=\"margin-right: 20px;\">Change an elements!</a>")
    11. fmt.Fprintf(w, "<a href=\"/insert\" style=\"margin-right: 20px;\">Insert an elements!</a>")
    12. fmt.Fprintf(w, "<h1>The contents of the KV store are:</h1>")
    13. fmt.Fprintf(w, "<ul>")
    14. for k, v := range DATA {
    15. fmt.Fprintf(w, "<li>")
    16. fmt.Fprintf(w, "<strong>%s</strong> with value: %v\n", k, v)
    17. fmt.Fprintf(w, "</li>")
    18. }
    19. fmt.Fprintf(w, "</ul>")
    20. }

    listAll() 函数没有使用任何 Go 模版来生成动态输出,而是使用 Go 动态生成的。您可以把这当作一个例外,因为 web 应用通常使用 HTML 模版和 html/templates标准库比较好。

    kvWeb.go的第四部分包含如下代码:

    从上面这段代码,您能看到在 FormValue() 函数的帮助下怎么读取一个 HTML 表单中字段的值。这个 template.Must() 函数是一个帮助函数,用于确保提供的模版文件不包含错误。

    1. func insertElement(w http.ResponseWriter, r *http.Request){
    2. fmt.Println("Inserting an element to the KV store!")
    3. tmpl := template.Must(template.ParseFiles("insert.gohtml"))
    4. if r.Method != http.MethodPost {
    5. tmpl.Execute(w, nil)
    6. return
    7. }
    8. key := r.FormValue("key")
    9. n := myElement {
    10. Name: r.FormValue("name"),
    11. Surname: r.FormValue("surname"),
    12. Id: r.FormValue("id"),
    13. }
    14. if !ADD(key, n) {
    15. fmt.Println("Add operation failed!")
    16. } else {
    17. err := save()
    18. if err != nil {
    19. fmt.Println(err)
    20. return
    21. }
    22. tmpl.Execute(w, struct{Success bool}{true})
    23. }
    24. }

    余下代码如下:

    1. func main() {
    2. err := load()
    3. if err != nil {
    4. fmt.Println(err)
    5. }
    6. PORT := ":8001"
    7. arguments := os.Args
    8. if len(arguments) == 1 {
    9. fmt.Println("Using default port number: ", PORT)
    10. } else {
    11. PORT = ":" + arguments[1]
    12. }
    13. http.HandleFunc("/", homePage)
    14. http.HandleFunc("/list", listAll)
    15. http.HandleFunc("/insert", insertElement)
    16. err = http.ListenAndServe(PORT, nil)
    17. if err != nil {
    18. fmt.Println(err)
    19. }
    20. }

    这个 kvWeb.go 的 函数要比第八章(Go UNIX系统编程)的 kvSaveLoad.gomain() 函数简单,因为这俩个程序有完全不同的设计。

    现在看一下 gohtml 文件,这个工程使用的起始文件是如下的 home.gohtml

    这个 home.gohtml 文件是静态的,就是说它的内容没有改变。而,其他的 gohtml 文件是动态显示信息。

    update.gohtml 的内容如下:

    1. <!doctype html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>A Key Value Store!</title>
    6. </head>
    7. <body>
    8. <a href="/" style="margin-right: 20px;">Home sweet home!</a>
    9. <a href="/list" style="margin-right: 20px;">List all elements!</a>
    10. <a href="/change" style="margin-right: 20px;">Change an elements!</a>
    11. <a href="/insert" style="margin-right: 20px;">Insert an elements!</a>
    12. {{if .Success}} <h1>Element updated!<h1>{{else}}
    13. <h1>Please fill in the fields:</h1>
    14. <form method="POST">
    15. <label>Key:</label><br/>
    16. <input type="text" name="key"><br/>
    17. <label>Name:</label><br/>
    18. <input type="text" name="name"><br/>
    19. <label>Surname:</label><br/>
    20. <input type="text" name="surname"><br/>
    21. <label>Id:</label><br/>
    22. <input type="text" name="id"><br/>
    23. <input type="submit">
    24. </form>
    25. {{end}}
    26. </body>
    27. </html>

    上面是主要的 HTML 代码。它最有趣的部分是 if 声明,它定义了您应该看到表单还是 Element updated! 信息。

    最后,insert.gohtml 的内容如下:

    1. <!doctype html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>A Key Value Store!</title>
    6. </head>
    7. <body>
    8. <a href="/" style="margin-right: 20px;">Home sweet home!</a>
    9. <a href="/list" style="margin-right: 20px;">List all elements!</a>
    10. <a href="/change" style="margin-right: 20px;">Change an elements!</a>
    11. <a href="/insert" style="margin-right: 20px;">Insert an elements!</a>
    12. {{if .Success}}
    13. <h1>Element inserted!</h1>
    14. {{else}}
    15. <h1>Please fill in the fields:</h1>
    16. <form method="POST">
    17. <label>Key:</label><br/>
    18. <input type="text" name="key"><br/>
    19. <label>Name:</label><br/>
    20. <input type="text" name="name"><br/>
    21. <label>Surname:</label><br/>
    22. <input type="text" name="surname"><br/>
    23. <label>Id:</label><br/>
    24. <input type="text" name="id"><br/>
    25. <input type="submit">
    26. </form>
    27. {{end}}
    28. </body>
    29. </html>

    您能从 <title> 标签的内容看出,insert.gohtmlupdate.gohtml 是完全相同的!

    在 Unix 命令行中执行 kvWeb.go 将产生如下输出:

    下面的截屏显示的是 key-value 存储的内容:

    key-value 存储的内容

    下面的截屏显示的是使用 kvWeb.go 应用的 web 接口添加新的数据到 key-value 存储中。

    下面的截屏显示的是使用 kvWeb.go 应用的 web 接口更新已存在 key 数据的值。

    更新 key-value 存储中一个 key 的值

    这个 kvWeb.go web 应用还不够完美,所以把它作为一个练习尽可能完善它。