5.15. Web服务器

现在让我们来实现一个完整的程序:一个简单的web服务器。这其实是一个转发服务器。 google的http://chart.apis.google.com 提供了一个将数据转换为图表的服务。不过那个图表的转换程序使用比较复杂,因为需要用户 自己设置各种参数。不过我们这里的程序界面要稍微友好一点:因为我们只需要获取一小段数据, 然后调用google的图表转换程序生存QR码(Quick Response缩写,二维条码),对于文本 信息下编码。二维条码图像可以用手机上的摄像机采集,然后解析得到解码后的信息。

下面是完整的程序:

  package main

  import (
      "flag"
      "http"
      "io"
      "log"
      "strings"
      "template"
  )

  var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
  var fmap = template.FormatterMap{
      "html": template.HTMLFormatter,
      "url+html": UrlHtmlFormatter,
  }
  var templ = template.MustParse(templateStr, fmap)

  func main() {
      flag.Parse()
      http.Handle("/", http.HandlerFunc(QR))
      err := http.ListenAndServe(*addr, nil)
      if err != nil {
          log.Exit("ListenAndServe:", err)
      }
  }

  func QR(c *http.Conn, req *http.Request) {
      templ.Execute(req.FormValue("s"), c)
  }

  func UrlHtmlFormatter(w io.Writer, v interface{}, fmt string) {
      template.HTMLEscape(w, strings.Bytes(http.URLEscape(v.(string))))
  }

  const templateStr = `
  <html>
      <head>
          <title>QR Link Generator</title>
      </head>
  <body>
      {.section @}
      <img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={@|url+html}"
  />
      <br>
      {@|html}
      <br>
      <br>
      {.end}
      <form action="/" name=f method="GET">
          <input maxLength=1024 size=70 name=s value="" title="Text to QR Encode">
          <input type=submit value=&##34;Show QR" name=qr>
      </form>
  </body>
  </html>
  `

main函数的开始部分比较简单。有一个flag选项用于指定HTTP服务器的监听端口。 还有一个模板变量templ,主要用于保存HTML页面的生成模板,我们稍后会讨论。

main首先分析命令行选项,然后帮定QR函数为服务器的根目录 处理函数。最后http.ListenAndServe启动服务器,并在服务器运行期间一直 阻塞。

QR接收客户端请求,然后用表单中的s变量的值替换到模板。

template包实现了json-template。 我们的程序的简洁正是得益于template包的强大功能。本质上,在执行templ.Execute的 时候,根据需要替换调模板中的某些区域。这里的原始模板文本保存在templateStr中, 其中花括弧部分对应模板的动作。在{.section @}和{.end}之间的 以@开头的元素,在处理模板的时候会被替换。

标记{@|url+html}的意思是在格式化模板的时候,用格式化字典(fmap) 中"url+html" 关键字对应的函数的处理标签的替代文本。这里的UrlHtmlFormatter 函数,只是为了安全启见过滤包含的不合法信息。

这里的模板只是用于显式的html页面。如果觉得上面的解释比较简略的话,可以看到template包的 documentation。

我们仅仅用很少的代码加一些HTML文本就实现了一个有意思的webserver。使用go,往往用很少的 代码就能实现强大的功能。