## 5.15\. Web服务器
现在让我们来实现一个完整的程序:一个简单的web服务器。这其实是一个转发服务器。 google的[http://chart.apis.google.com](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,往往用很少的 代码就能实现强大的功能。
- 1. 关于本文
- 2. Go语言简介
- 3. 安装go环境
- 3.1. 简介
- 3.2. 安装C语言工具
- 3.3. 安装Mercurial
- 3.4. 获取代码
- 3.5. 安装Go
- 3.6. 编写程序
- 3.7. 进一步学习
- 3.8. 更新go到新版本
- 3.9. 社区资源
- 3.10. 环境变量
- 4. Go语言入门
- 4.1. 简介
- 4.2. Hello,世界
- 4.3. 分号(Semicolons)
- 4.4. 编译
- 4.5. Echo
- 4.6. 类型简介
- 4.7. 申请内存
- 4.8. 常量
- 4.9. I/O包
- 4.10. Rotting cats
- 4.11. Sorting
- 4.12. 打印输出
- 4.13. 生成素数
- 4.14. Multiplexing
- 5. Effective Go
- 5.1. 简介
- 5.2. 格式化
- 5.3. 注释
- 5.4. 命名
- 5.5. 分号
- 5.6. 控制流
- 5.7. 函数
- 5.8. 数据
- 5.9. 初始化
- 5.10. 方法
- 5.11. 接口和其他类型
- 5.12. 内置
- 5.13. 并发
- 5.14. 错误处理
- 5.15. Web服务器
- 6. 如何编写Go程序
- 6.1. 简介
- 6.2. 社区资源
- 6.3. 新建一个包
- 6.4. 测试
- 6.5. 一个带测试的演示包
- 7. Codelab: 编写Web程序
- 7.1. 简介
- 7.2. 开始
- 7.3. 数据结构
- 7.4. 使用http包
- 7.5. 基于http提供wiki页面
- 7.6. 编辑页面
- 7.7. template包
- 7.8. 处理不存在的页面
- 7.9. 储存页面
- 7.10. 错误处理
- 7.11. 模板缓存
- 7.12. 验证
- 7.13. 函数文本和闭包
- 7.14. 试试!
- 7.15. 其他任务
- 8. 针对C++程序员指南
- 8.1. 概念差异
- 8.2. 语法
- 8.3. 常量
- 8.4. Slices(切片)
- 8.5. 构造值对象
- 8.6. Interfaces(接口)
- 8.7. Goroutines
- 8.8. Channels(管道)
- 9. 内存模型
- 9.1. 简介
- 9.2. Happens Before
- 9.3. 同步(Synchronization)
- 9.4. 错误的同步方式
- 10. 附录
- 10.1. 命令行工具
- 10.2. 视频和讲座
- 10.3. Release History
- 10.4. Go Roadmap
- 10.5. 相关资源