返回介绍

12.7.2 用 Go 创建网站

发布于 2024-08-14 12:50:31 字数 17787 浏览 0 评论 0 收藏 0

您还记得 第四章 (组合类型的使用)中的 keyValue.go 应用,和 第八章 (Go UNIX系统编程)中的 kvSaveLoad.go吗?在这节,您将学习使用 Go 标准库给它们创建 web 接口。这个新的 Go 源代码文件命名为 kvWeb.go,并分为6部分展示。

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

kvWeb.go 的第一部分如下:

package main

import (
    "encoding/gob"
    "fmt"
    "html/template"
    "net/http"
    "os"
)

type myElement struct {
    Name    string
    Surname string
    Id      string
}

var DATA = make(map[string]myElement)
var DATAFILE = "/tmp/dataFile.gob"

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

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

func save() error {
    fmt.Println("Saving", DATAFILE)
    err := os.Remove(DATAFILE)
    if err != nil {
        fmt.Println(err)
    }
    saveTo, err := os.Create(DATAFILE)
    if err != nil {
        fmt.Println("Cannot create", DATAFILE)
        return err
    }
    defer saveTo.Close()

    encoder := gob.NewEncoder(saveTo)
    err = encoder.Encode(DATA)
    if err != nil {
        fmt.Println("Cannot save to", DATAFILE)
        return err
    }
    return nil
}

func load() error {
    fmt.Println("Loading", DATAFILE)
    loadFrom, err := os.Open(DATAFILE)
    defer loadFrom.Close()
    if err != nil {
        fmt.Println("Empty key/value store!")
        return err
    }

    decoder := gob.NewDecoder(loadFrom)
    decoder.Decode(&DATA)
    return nil
}

func ADD(k string, n myElement) bool {
    if k == "" {
        return false
    }

    if LOOKUP(k) == nil {
        DATA[k] = n
        return true
    }
    return false
}

func DELETE(k string) bool {
    if LOOKUP(k) != nil {
        delete(DATA, k)
        return true
    }
    return false
}

func LOOKUP(k string) *myElement {
    _, ok := DATA[k]
    if ok {
        n := DATA[k]
        return &n
    } else {
        return nil
    }
}

func CHANGE(k string, n myElement) bool {
    DATA[k] = n
    return true
}

func PRINT() {
    for k, d := range DATA {
        fmt.Println("key: %s value: %v\n", k, d)
    }
}

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

kvWeb.go 的第三部分如下:

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Serving", r.Host, "for", r.URL.Path)
    myT := template.Must(template.ParseGlob("home.gohtml"))
    myT.ExecuteTemplate(w, "home.gohtml", nil)
}

func listAll(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Listing the contents of the KV store!")
    fmt.Fprintf(w, "<a href=\"/\" style=\"margin-right: 20px;\">Home sweet home!</a>")
    fmt.Fprintf(w, "<a href=\"/list\" style=\"margin-right: 20px;\">List all elements!</a>")
    fmt.Fprintf(w, "<a href=\"/change\" style=\"margin-right: 20px;\">Change an elements!</a>")    
    fmt.Fprintf(w, "<a href=\"/insert\" style=\"margin-right: 20px;\">Insert an elements!</a>")    

    fmt.Fprintf(w, "<h1>The contents of the KV store are:</h1>")
    fmt.Fprintf(w, "<ul>")
    for k, v := range DATA {
        fmt.Fprintf(w, "<li>")
        fmt.Fprintf(w, "<strong>%s</strong> with value: %v\n", k, v)
        fmt.Fprintf(w, "</li>")
    }
    fmt.Fprintf(w, "</ul>")
}

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

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

func changeElement(w http.ResponseWriter, r *http.Request){
    fmt.Println("Change an element of the KV store!")
    tmpl := template.Must(template.ParseFiles("update.gohtml"))
    if r.Method != http.MethodPost {
        tmpl.Execute(w, nil)
        return
    }

    key := r.FormValue("key")
    n := myElement{
        Name:    r.FormValue("name"),
        Surname: r.FormValue("surname"),
        Id:      r.FormValue("id"),
    }

    if !CHANGE(key, n) {
        fmt.Println("Update operation failed!")
    } else {
        err := save()
        if err != nil {
            fmt.Println(err)
            return
        }
        tmpl.Execute(w, struct {Struct bool}{true})
    }
}

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

kvWeb.go 的第五段代码如下:

func insertElement(w http.ResponseWriter, r *http.Request){
    fmt.Println("Inserting an element to the KV store!")
    tmpl := template.Must(template.ParseFiles("insert.gohtml"))
    if r.Method != http.MethodPost {
        tmpl.Execute(w, nil)
        return
    }

    key := r.FormValue("key")
    n := myElement {
        Name:    r.FormValue("name"),
        Surname: r.FormValue("surname"),
        Id:      r.FormValue("id"),
    }

    if !ADD(key, n) {
        fmt.Println("Add operation failed!")
    } else {
        err := save()
        if err != nil {
            fmt.Println(err)
            return
        }
        tmpl.Execute(w, struct{Success bool}{true})
    }
}

余下代码如下:

func main() {
    err := load()
    if err != nil {
        fmt.Println(err)
    }

    PORT := ":8001"
    arguments := os.Args
    if len(arguments) == 1 {
        fmt.Println("Using default port number: ", PORT)
    } else {
        PORT = ":" + arguments[1]
    }

    http.HandleFunc("/", homePage)
    http.HandleFunc("/change", changeElement)
    http.HandleFunc("/list", listAll)
    http.HandleFunc("/insert", insertElement)
    err = http.ListenAndServe(PORT, nil)
    if err != nil {
        fmt.Println(err)
    }
}

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

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

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>A Key Value Store!</title>
</head>
<body>

<a href="/" style="margin-right: 20px;">Home sweet home!</a>
<a href="/list" style="margin-right: 20px;">List all elements!</a>
<a href="/change" style="margin-right: 20px;">Change an elements!</a>
<a href="/insert" style="margin-right: 20px;">Insert an elements!</a>

<h2>Welcome to the Go KV store!</h2>
</body>
</html>

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

update.gohtml 的内容如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>A Key Value Store!</title>
</head>
<body>

<a href="/" style="margin-right: 20px;">Home sweet home!</a>
<a href="/list" style="margin-right: 20px;">List all elements!</a>
<a href="/change" style="margin-right: 20px;">Change an elements!</a>
<a href="/insert" style="margin-right: 20px;">Insert an elements!</a>

{{if .Success}} <h1>Element updated!<h1>{{else}}
<h1>Please fill in the fields:</h1>
    <form method="POST">
        <label>Key:</label><br/>
        <input type="text" name="key"><br/>
        <label>Name:</label><br/>
        <input type="text" name="name"><br/>
        <label>Surname:</label><br/>
        <input type="text" name="surname"><br/>
        <label>Id:</label><br/>
        <input type="text" name="id"><br/>
        <input type="submit">
    </form>
{{end}}

</body>
</html>

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

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

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>A Key Value Store!</title>
</head>
<body>

<a href="/" style="margin-right: 20px;">Home sweet home!</a>
<a href="/list" style="margin-right: 20px;">List all elements!</a>
<a href="/change" style="margin-right: 20px;">Change an elements!</a>
<a href="/insert" style="margin-right: 20px;">Insert an elements!</a>

{{if .Success}}
    <h1>Element inserted!</h1>
{{else}}
    <h1>Please fill in the fields:</h1>
    <form method="POST">
        <label>Key:</label><br/>
        <input type="text" name="key"><br/>
        <label>Name:</label><br/>
        <input type="text" name="name"><br/>
        <label>Surname:</label><br/>
        <input type="text" name="surname"><br/>
        <label>Id:</label><br/>
        <input type="text" name="id"><br/>
        <input type="submit">
    </form>
{{end}}

</body>
</html>

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

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

$ go run kvWeb.go
Loading /tmp/dataFile.gob
Using default port number:  :8001
Serving localhost:8001 for /
Serving localhost:8001 for /favicon.ico
Listing the contents of the KV store!
Serving localhost:8001 for /favicon.ico
Inserting an element to the KV store!
Serving localhost:8001 for /favicon.ico
Inserting an element to the KV store!
Add operation failed!
Serving localhost:8001 for /favicon.ico
Inserting an element to the KV store!
Serving localhost:8001 for /favicon.ico
Inserting an element to the KV store!
Saving /tmp/dataFile.gob
Serving localhost:8001 for /favicon.ico
Inserting an element to the KV store!
Serving localhost:8001 for /favicon.ico
Changing an element of the KV store!
Serving localhost:8001 for /vavicon.ico

另外,真正有趣的是您可以用 web 浏览器和 kvWeb.go 交互。这个网站的首页,定义在 home.gohtml 显示截屏如下:

web 应用的静态首页

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

key-value 存储的内容

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

添加一个新数据到 key-value 存储中

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

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

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

这节说明了您如何使用 Go 开放整个网址和 web 应用。尽管您的需求无疑是多样的,但是和 kvWeb.go 使用的技术是相同的。注意,自己做的网站被认为要比那些由流行的管理系统创建的更安全。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文