Files
the-way-to-go_ZH_CN/eBook/19.4.md
marjune 1dfd41bd93 add chapter 19.x (#700)
* add chapter 19.x

* add chapter 19.4
2019-08-01 21:16:46 -07:00

124 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 19.4 用户界面web 服务端
(本节代码见 [goto_v1/main.go](examples/chapter_19/goto_v1/main.go)。)
我们尚未编写启动程序的必要函数。它们(总是)类似 CC++ 或 Java 中的 `main()` 函数,我们的 web 服务器由它启动,例如用如下命令在本地 8080 端口启动 web 服务器:
```go
http.ListenAndServe(":8080", nil)
```
web 服务器的功能来自于 `http` 包,[15 章](15.0.md) 做了深入介绍。web 服务器会在一个无限循环中监听到来的请求,但我们必须定义针对这些请求,服务器该如何响应。可以用被称为 HTTP 处理器的 `HandleFunc` 函数来办到,例如代码:
```go
http.HandleFunc("/add", Add)
```
如此,每个以 `/add` 结尾的请求都会调用 `Add` 函数(尚未完成)。
程序有两个 HTTP 处理器:
- `Redirect`,用于对短 URL 重定向
- `Add`,用于处理新提交的 URL
示意图:
![](images/19.4_fig19.1.jpg?raw=true)
最简单的 `main()` 函数类似这样:
```go
func main() {
http.HandleFunc("/", Redirect)
http.HandleFunc("/add", Add)
http.ListenAndServe(":8080", nil)
}
```
`/add` 的请求由 `Add` 处理器处理,所有其他请求会被 `Redirect` 处理器处理。处理函数从到来的请求(一个类型为 `*http.Request` 的变量)中获取信息,然后产生响应并写入 `http.ResponseWriter` 类型变量 `w`
`Add` 函数必须做的事有:
1. 读取长 URL`r.FormValue("url")` 从 HTML 表单提交的 HTTP 请求中读取 URL
2. 使用 store 上的 `Put` 方法存储长 URL
3. 将对应的短 URL 发送给用户
每个需求都转化为一行代码:
```go
func Add(w http.ResponseWriter, r *http.Request) {
url := r.FormValue("url")
key := store.Put(url)
fmt.Fprintf(w, "http://localhost:8080/%s", key)
}
```
这里 `fmt` 包的 `Fprintf` 函数用来替换字符串中的关键字 `%s`,然后将结果作为响应发送回客户端。注意 `Fprintf` 把数据写到了 `ResponseWriter` 中,其实 `Fprintf` 可以将数据写到任何实现了 `io.Writer` 的数据结构,即该结构实现了 `Write` 方法。Go 中 `io.Writer` 称为接口,可见 `Fprintf` 利用接口变得十分通用可以对很多不同的类型写入数据。Go 中接口的使用十分普遍,它使代码更通用(见 [11 章](11.0.md))。
还需要一个表单,仍然可以用 `Fprintf` 来输出,这次将常量写入 `w`。让我们来修改 `Add`,当未指定 URL 时显示 HTML 表单:
```go
func Add(w http.ResponseWriter, r *http.Request) {
url := r.FormValue("url")
if url == "" {
fmt.Fprint(w, AddForm)
return
}
key := store.Put(url)
fmt.Fprintf(w, "http://localhost:8080/%s", key)
}
const AddForm = `
<form method="POST" action="/add">
URL: <input type="text" name="url">
<input type="submit" value="Add">
</form>
`
```
在那种情况下,发送字符串常量 `AddForm` 到客户端,它是 html 表单,包含一个 `url` 输入域和一个提交按钮,点击后发送 POST 请求到 `/add`。这样 `Add` 处理函数被再次调用,此时 `url` 的值来自文本域。(` `` ` 用来创建原始字符串,否则按惯例 `""` 将成为字符串边界。)
`Redirect` 函数在 HTTP 请求路径中找到键(短 URL 的键是请求路径去除首字符,在 Go 中可以写为 `[1:]`。例如请求 "/abc",键就是 "abc"),用 `Get` 函数从 `store` 检索到对应的长 URL对用户发送 HTTP 重定向。如果没找到 URL发送 404 "Not Found" 错误取而代之:
```go
func Redirect(w http.ResponseWriter, r *http.Request) {
key := r.URL.Path[1:]
url := store.Get(key)
if url == "" {
http.NotFound(w, r)
return
}
http.Redirect(w, r, url, http.StatusFound)
}
```
`http.NotFound``http.Redirect` 是发送通用 HTTP 响应的工具函数。)
我们已经完整地遍历了 [goto_v1](examples/chapter_19/goto_v1) 的代码。
## 编译和运行
可执行程序已包含在示例代码下,如果你想立即测试可以跳过本节。其中包含 3 个 go 源文件和一个 Makefile 文件,通过它应用可以被编译和链接,只须如下操作:
- **Linux 和 OSX 平台:** 在终端窗口源码目录下启动 `make` 命令,或在 LiteIDE 中构建项目。
- **Windows 平台:** 启动 MINGW 环境步骤为开始菜单所有程序MinGWMinGW Shell见 [2.5.5 节](02.5.md)),在命令行窗口输入 `make` 并回车,源代码被编译并链接为原生 exe 可执行程序。
生成内容为可执行程序Linux/OS X 下为 `goto`Windows 下为 `goto.exe`
要启动并运行 web 服务器,那么:
- **Linux 和 OSX 平台:** 输入命令 `./goto`
- **Windows 平台:** 从 Go IDE 启动程序(如果 Windows 防火墙阻止程序启动,设置允许该程序)
## 测试该程序
打开浏览器并请求 url`http://localhost:8080/add`
这会激活 `Add` 处理函数。请求还未包含 url 变量,所以响应会输出 html 表单询问输入:
![](images/19.4_fig19.2.png?raw=true)
添加一个长 URL 以获取等价的缩短版本,例如 `http://golang.org/pkg/bufio/#Writer`,然后单击按钮。应用会为你产生一个短 URL 并打印出来,例如 `http://
localhost:8080/2`
![](images/19.4_fig19.3.jpg?raw=true)
复制该 URL 并在浏览器地址栏粘贴以发出请求,现在轮到 `Redirect` 处理函数上场了,对应长 URL 的页面被显示了出来。
![](images/19.4_fig19.4.jpg?raw=true)
## 链接
- [目录](directory.md)
- 上一节:[数据结构](19.3.md)
- 下一节:[持久化存储gob](19.5.md)