MENU

翻译golang官网文章Writing Web Applications

January 17, 2021 • Read: 958 • 默认分类阅读设置

原文: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: January 18, 2021