- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
12.7.2 用 Go 创建网站
您还记得 第四章 (组合类型的使用)中的 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.go
的 main()
函数要比 第八章 (Go UNIX系统编程)的 kvSaveLoad.go
的 main()
函数简单,因为这俩个程序有完全不同的设计。
现在看一下 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.gohtml
和 update.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
显示截屏如下:
下面的截屏显示的是 key-value 存储的内容:
下面的截屏显示的是使用 kvWeb.go
应用的 web 接口添加新的数据到 key-value 存储中。
下面的截屏显示的是使用 kvWeb.go
应用的 web 接口更新已存在 key 数据的值。
这个 kvWeb.go
web 应用还不够完美,所以把它作为一个练习尽可能完善它。
这节说明了您如何使用 Go 开放整个网址和 web 应用。尽管您的需求无疑是多样的,但是和
kvWeb.go
使用的技术是相同的。注意,自己做的网站被认为要比那些由流行的管理系统创建的更安全。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论