[本章节的代码可以在 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 。

    示例图:

    19.4  用户界面:Web 网页前端 - 图1

    我们最小的 main () 可能看起来像这样:

    1. func main() {
    2. http.HandleFunc("/", Redirect)
    3. http.HandleFunc("/add", Add)
    4. http.ListenAndServe(":8080", nil)
    5. }

    请求 /add 将被 Add 处理器处理;所有的其他请求将被 Redirect 处理器处理。处理器函数从一个传入的请求(一个 *http. Request 变量)获取信息,并且将他们的响应写入一个 http.ResponseWriter 类型的变量 w 中。

    我们的 Add 函数必须做什么?

    i)读取长 URL,即: 通过 r.FormValue("url") ,从一个 HTTP 请求包含的 html 表单读取。

    ii) 在 store (译者注: NewURLStore () 返回的变量) 上使用我们的 Put 方法将它保存。

    iii) 发送相应的短 URL 给用户

    每个需求在代码行中的翻译:

    1. func Add(w http.ResponseWriter, r *http.Request) {
    2. url := r.FormValue("url")
    3. key := store.Put(url)
    4. fmt.Fprintf(w, "http://localhost:8080/%s", key)
    5. }

    fmt 包的 Fprintf 函数用来在包含 % s 的字符串中替换 key,然后将这个字符串作为响应发送回客户端。

    请注意: Fprintf 写入一个 ResponseWriter,其实 Fprintf 能写入任何实现了 io.Writer() 的数据结构体,这意味着它实现了一个 Write () 方法。io.Writer () 是 Go 中的一个接口,并且我们看到,通过接口的使用,Fprintf 是非常的通用,它能写入很多不同的东西。在 Go 中,接口的使用是无处不在的,它使代码变得更加通用( 参见 章节 11 )。

    但是我们仍然需要一个表单,我们可以再次使用 Fprintf 显示一个表单,这次给 w 写入一个常量。当没有提供 url 的时候,我们修改 Add 去显示一个 HTML 表单:

    1. func Add(w http.ResponseWriter, r *http.Request) {
    2. url := r.FormValue("url")
    3. if url == "" {
    4. fmt.Fprint(w, AddForm)
    5. return
    6. }
    7. key := store.Put(url)
    8. fmt.Fprintf(w, "http://localhost:8080/%s", key)
    9. }
    10. const AddForm = `
    11. <form method="POST" action="/add">
    12. URL: <input type="text" name="url">
    13. <input type="submit" value="Add">
    14. </form>
    15. `

    在这种情况下,我们发送一个常量字符串 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" 错误:

    1. func Redirect(w http.ResponseWriter, r *http.Request) {
    2. key := r.URL.Path[1:]
    3. url := store.Get(key)
    4. if url == "" {
    5. http.NotFound(w, r)
    6. return
    7. }
    8. http.Redirect(w, r, url, http.StatusFound)
    9. }

    (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:

    localhost:8080/add

    在这开始我们的添加处理函数。 表单中还没有任何 url 变量,所以响应的是 html 格式的请求输入:

    19.4  用户界面:Web 网页前端 - 图2

    添加一个你想要变短的长网址,例如:golang.org/pkg/bufio/#Writer ,并且按下 Add 按钮。应用程序为你制作了一个 短 URL 并打印出来,例如: localhost:8080/2

    19.4  用户界面:Web 网页前端 - 图3

    复制并粘贴这个 URL 到你的浏览器的地址栏中,然后访问它。它会执行一个重定向,并显示长 URL 的页面。

    19.4  用户界面:Web 网页前端 - 图4

    版本 2 —- 添加持久化存储

    第 2 版的代码在 goto_v2 中( 在 章节 19.5 讨论 ),可以在 code_examples\ chapter_19\goto_v2 目录中找到它。