[本章节的代码可以在 goto_v1\main.go 中找到]
我们还没有编写我们程序启动所必须的功能。如在 C 、 C++ 、 Java 中,这是(总是) main()
函数。例如,我们可以使用下面命令在 8080 端口上启动一个本地的 web 服务器: http.ListenAndServe(":8080", nil)
( web 服务器的功能来自 http 包,我们已经在 章节 15 深入讨论过了)。 web 服务器在一个无限循环中监听传入的请求,但是我们还必须去定义该服务器如何去响应这些请求。我们通过使用 HandleFunc 函数来创建所谓的 HTTP 处理程序来完成此操作,示例代码:
http.HandleFunc(“/add”, Add)
我们说每一个 /add
结束的请求都会调用一个 Add
函数(还没有定义)。
我们的程序将有两个 HTTP 处理器:
- Redirect, 重定向短网址的请求,以及
- Add,处理新提交的 URLs 。
示例图:
我们最小的 main () 可能看起来像这样:
func main() {
http.HandleFunc("/", Redirect)
http.HandleFunc("/add", Add)
http.ListenAndServe(":8080", nil)
}
请求 /add
将被 Add 处理器处理;所有的其他请求将被 Redirect 处理器处理。处理器函数从一个传入的请求(一个 *http. Request
变量)获取信息,并且将他们的响应写入一个 http.ResponseWriter
类型的变量 w
中。
我们的 Add 函数必须做什么?
i)读取长 URL,即: 通过 r.FormValue("url")
,从一个 HTTP 请求包含的 html 表单读取。
ii) 在 store
(译者注: NewURLStore () 返回的变量) 上使用我们的 Put
方法将它保存。
iii) 发送相应的短 URL 给用户
每个需求在代码行中的翻译:
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 的字符串中替换 key
,然后将这个字符串作为响应发送回客户端。
请注意: Fprintf 写入一个 ResponseWriter,其实 Fprintf 能写入任何实现了 io.Writer()
的数据结构体,这意味着它实现了一个 Write () 方法。io.Writer () 是 Go 中的一个接口,并且我们看到,通过接口的使用,Fprintf 是非常的通用,它能写入很多不同的东西。在 Go 中,接口的使用是无处不在的,它使代码变得更加通用( 参见 章节 11 )。
但是我们仍然需要一个表单,我们可以再次使用 Fprintf 显示一个表单,这次给 w
写入一个常量。当没有提供 url 的时候,我们修改 Add 去显示一个 HTML 表单:
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 给客户端,这实际上是创建了一个需要包含一个 form 表单的 html,form 中有一个 url 的 input 字段、一个提交按钮,当推送时将发送一个 /add
结尾的请求。所以 Add 处理器函数再次被调用,现在从文本字段获取了一个值。(``` 在制作一个原始的字符串时使用, 否则像往常一样将字符串括在
“ “` 中)。
Redirect 函数在 HTTP 请求路径中找到 key
(短 URL 的 key 是请求路径去除第一个字符,在 Go 中可以这样去写 [1:]
; 如 请求 /abc
, key 将是 abc
),通过 store
的 Get 函数取出相应的长 URL ,然后向用户发送一个 HTTP 重定向。如果 URL 没有找到,发送一个 404 "Not Found"
错误:
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
所有代码。
译者注: 因为 Go 从 1.5 版本开始已经实现 自举(Bootstrapping) ),所以下面的编译方式已经无法使用,个人觉得也就没有必要看了。Linux 下直接在源码所在目录执行
go build
就可以编译源码,默认会生成一个和目录名称相同的可执行文件(个人觉得 Windows 和 macOS 下应该一样,不过没有环境无法测试,请自行尝试或查看其他文档)。
PS: 下面的环境无法尝试,全部都是字面翻译,不一定准确……
编译并运行:
可执行文件在下载的示例文件中,如果你愿意,你可以直接跳过本节并立即测试。带有 3 个 go 源文件的目录中还有一个 MakeFile 文件,通过它们可以应用和链接应用程序,只需要这样做:
- 适用于 Linux、OS X: 从目录中打开一个终端窗口或者直接在
LiteIDE
的项目中构建 - 适用于 Windows :通过启动 MINGW 环境开始,所有程序、 MinGW 、 MinGW Shell (参见 章节 2.5 ),在命令行窗口输入 type:make 并进入:
源文件被编译并链接到一个 native .exe
结果是一个可执行程序: Linux/OS X 中是 goto
、 Windows 中是 goto.exe
。
要运行它并启动 web 服务器,执行:
- 适用于 Linux, OS X : 执行
./goto
命令 - 适用于 Windows : 从 GoIde 启动程序 (如果 Windows 防火墙阻止启动程序:设置允许此程序)
测试程序:
打开一个浏览器并请求下面的 URL:
在这开始我们的添加处理函数。 表单中还没有任何 url 变量,所以响应的是 html 格式的请求输入:
添加一个你想要变短的长网址,例如:golang.org/pkg/bufio/#Writer ,并且按下 Add 按钮。应用程序为你制作了一个 短 URL 并打印出来,例如: localhost:8080/2
复制并粘贴这个 URL 到你的浏览器的地址栏中,然后访问它。它会执行一个重定向,并显示长 URL 的页面。
版本 2 —- 添加持久化存储
第 2 版的代码在 goto_v2 中( 在 章节 19.5 讨论 ),可以在 code_examples\ chapter_19\goto_v2 目录中找到它。