MENU

翻译golang官网文章Writing Web Applications

January 17, 2021 • Read: 3868 • 默认分类

原文: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 (错误) 的值.

这个函数将会保存 PageBody 为一个 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 遇到了错误怎么办?举个例子,这个文件也许不存在,我们不应该忽略这类的错误,那咱们就修改一下这个函数,让它返回 *Pageerror.

  • 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!

未完待续

Last Modified: September 17, 2023
Leave a Comment

2 Comments
  1. sfvx sfvx

    98 礼品网负责礼品代发、快递代发、拍 A 发 B 快递、礼品等服务。http://www.98lp.cn

  2. 感谢分享 biubiu 赞一个