原文:https://golang.org/doc/articles/wiki/
应该有人翻译过了,不过边翻译边学边做,感觉还不错
介绍
在这个教程中包括了如下内容:
- 使用 load 与 save 方法创建一个数据结构
- 使用 net/http 包构建一个 web 应用程序
- 使用 html/template 包来处理 HTML 文件模板
- 使用 regexp 包验证用户输入
- 使用闭包
需要你具有:
- 编程经验
- 理解基本的 web 应用技术 (HTTP, HTML)
- 一些 UNIX/DOS 命令行基础
开始上手
现在,你需要一台 FreeBSD, Linux, OSX 或者 Windows 机器才能运行 Go, 在这里将会使用 $ 来代表命令行输入
安装 Go (查看 安装文档)
在你的 GOPATH 中为这个教程创建一个文件夹,并且 cd 进入这个文件夹中
- $ mkdir gowiki
- $ cd gowiki
创建一个名字为 wiki.go 的文件,用你最喜欢的编辑器打开这个文件,然后在这个文件中添加如下内容
- package main
-
- import (
- "fmt"
- "io/ioutil"
- )
在这里,我们从 Go 标准库中导入了 fmt 和 ioutil 包,稍后我们实现更多功能的时候,我们会使用 import 导入更多的包
数据结构
让我们从定义一个数据结构开始学起,一个 Wiki 页面由许多个网页组成,每一个网页都有一个标题 (title) 和一个正文 (body), 这里,我们定义了一个 Page
为一个结构,并且有两个字段表示标题和正文.
- type Page struct {
- Title string
- Body []byte
- }
[] byte 这个数据类型代表着 "1 比特
的切片 ".(查看 切片:使用方法与内部结构 获取更多有关切片的内容), 这个 Body
元素是一个 []byte
而不是一个 string
(字符串), 这是我们导入的 io 库中的内容,如下所示.
这个 Page
结构描述了一个页面的数据是怎么样存储在内存中的,但是用文件存储呢?我们可以在 Page
中创建一个 save
函数去指定这个文件的路径:
- func (p *Page) save() error {
- filename := p.Title + ".txt"
- return ioutil.WriteFile(filename, p.Body, 0600)
- }
这个函数的解释如下: " 这是一个叫做 save
的函数,这个函数将指向 Page
的指针 p
作为其接收器,它不需要任何参数,然后 return (返回) 一个类型为 error (错误) 的值.
这个函数将会保存 Page
的 Body
为一个 txt 文件,为了简单起见,我们这里使用了 Title
作为这个文件的名字.
这个 save
函数之所以会 return (返回) 一个 error
值是因为其 WriteFile (一个将数据切片保存为文件的函数封装的标准库) 的返回值就是 error
, 这个 save
函数返回了一个错误值的意思是如果在写入文件时出现任何错误,就让应用程序来处理,如果一切进展顺利,Page.save()
会 return 一个 nil
(空指针,接口,或者一些其它的类型).
最后的八进制整数 0600
, 作为第三个参数传递给 WriteFile
, 指示了这个文件的权限,0600
为只有当前用户拥有的 read-write (读写) 权限 (详情请查看 Unix man page open(2)
)
除了保存页面,我们还希望它加载页面:
- func loadPage(title string) *Page {
- filename := title + ".txt"
- body, _ := ioutil.ReadFile(filename)
- return &Page{Title: title, Body: body}
- }
这个 loadPage
函数使用 title 参数构造文件名,读取文件内容,并赋值给一个变量 body
, 然后 return 一个指针,指向用适当的 title 和 body 值构造的 Page
函数可以返回多个值,标准库函数 io.ReadFile
会返回一个 []byte
和一个 error
, 在 loadPage
中,错误还没有被处理,由下划线 (_) 表示的 "空标识符" 用于丢弃错误返回值 (本质上,赋值为空)
但是如果 ReadFile
遇到了错误怎么办?举个例子,这个文件也许不存在,我们不应该忽略这类的错误,那咱们就修改一下这个函数,让它返回 *Page
和 error
.
- func loadPage(title string) (*Page, error) {
- filename := title + ".txt"
- body, error := ioutil.ReadFile(filename)
- if err != nil {
- return nil, err
- }
- return &Page{Title: title, Body: body}, nil
- }
这个函数的使用者可以使用第二个参数做检查了,如果是 nil
那就代表着它正确的成功的加载了一个页面,如果不是 nil
, 这就会是一个由函数使用者处理的错误
在这里我们已经有了一个简单的数据结构和从文件保存加载的能力,让我们写出一个 main
函数来测试一下我们刚刚写的内容:
- func main() {
- p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
- p1.save()
- p2, _ := loadPage("TestPage")
- fmt.Println(string(p2.Body))
- }
在编译和执行代码之后,目录下会有一个名字为 TestPage.txt 的文件,这个文件包含了 p1 的内容,然后将文件读入 p2
, 最后将 Body
元素输出到窗口中.
你可以使用如下命令来编译执行代码:
- $ go build wiki.go
- $ ./wiki
- This is a sample Page
(如果你在使用 Windows 系统的话,你必须输入 "wiki
", 而没有"./", 来运行这个文件.)
如下是我们到现在所写的全部代码
- //Copyright 2010 The Go Authors. All rights reserved.
- //Use of this source code is governed by a BSD-style
- //license that can be found in the LICENSE file.
-
- // +build ignore
- package main
-
- import (
- "fmt"
- "io/ioutil"
- )
-
- type Page struct {
- Title string
- Body []byte
- }
-
- func (p *Page) save() error {
- filename := p.Title + ".txt"
- return ioutil.WriteFile(filename, p.Body, 0600)
- }
-
- func loadPage(title string) (*Page, error) {
- filename := title + ".txt"
- body, error := ioutil.ReadFile(filename)
- if err != nil {
- return nil, err
- }
- return &Page{Title: title, Body: body}, nil
- }
-
- func main() {
- p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
- p1.save()
- p2, _ := loadPage("TestPage")
- fmt.Println(string(p2.Body))
- }
介绍 net/http包(插曲)
这里是一个完整的正常工作的简单 web 服务器代码:
- // +build ignore
-
- package main
-
- import (
- "fmt"
- "log"
- "net/http"
- )
-
- func handler(w http.ResponseWrite, r *http.Request) {
- fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
- }
-
- func main() {
- http.HandleFunc("/", handler)
- log.Fatal(http.ListenAndServe(":8080", nil))
- }
这个 main
函数从 http.HandleFunc
开始,这个函数告诉了 http
包用 handler
去处理所有到 /
(网站根目录) 下的网络请求
然后呼叫了 http.ListenAndServe
, 指定了它应该监听任何接口上的 8080 端口 (现在还不用担心传递的第二个参数 nil
) 这个函数会一直阻止程序自我终止
ListenAndServe
总是 return 一个 error, 它只在发生意外错误的时候返回,我们为了记录下发生的错误,会使用 log.Fatal
.
handler
函数的类型为 http.HandlerFunc
, 它需要 http.ResponseWriter
和一个 http.Request
作为它的参数.
http.ResponseWriter
值聚集了 HTTP 服务器的响应,通过向其写入,就可以将数据发送给 HTTP 客户端.
一个 http.Request
就是一个数据结构,这个数据结构表示这客户端的 HTTP 请求,r.URL.Path
就是请求的 URL 路径,尾巴后面的 [1:] 得意思是 "用请求的路径 / 之后所有的字符创建一个子 Path 的子切片"
如果你用浏览器访问如下网址:
- http://localhost:8080/monkeys
这个程序就会在页面中呈现出:
- Hi there, I love monkeys!
未完待续
98 礼品网负责礼品代发、快递代发、拍 A 发 B 快递、礼品等服务。http://www.98lp.cn
感谢分享 biubiu 赞一个