Fork me on GitHub

Go语言开发-布尔与数值类型-实现一个HTTP服务器

2.4.2.实现一个基本的HTTP服务器

这个statistics程序仅有一个web页面,并运行在本机上,它的main()函数如下:

Http.HandleFunc() 函数接受两个参数:路径和当该路径被请求时要调用的函数的引用。该函数必须具有签名 func(http.ResponseWriter, * http. Request)。如果我们愿意,我们可以注册多个“路径-函数”对。这里我们只注册了路径/(即web程序的主页)和一个自定义的homePage() 函数。

Http.ListenAndServe()函数以给定的TCP网络地址启动一个web服务器;这里我们使用的是localhost和9001端口。对于本机来说如果只指定了端口号,那么我们可以使用”localhost:9001”或者”127.0.0.1:9001” 地址。(我们可以指定任意的端口号,如果它与现有的服务器相冲突,修改代码中的端口为其它端口号即可)该函数的第二个参数用于指定使用哪种类型的服务器,通常我们传一个nil参数来表示使用默认的类型。

该程序使用了一些字符串常量,但是这里我们只展示其中的一个。

字符串常量form含有一个<form>元素,同时该元素也包含一些文本和提交按钮<input>元素。

 

当statistics web站点被访问时该函数被调用,我们可以将响应信息(HTML形式)写入到writer参数中,而request参数包含了请求的详细信息。

我们先来分析这个表单,该表单起初有一个空的<input>元素。我们将该元素命名为”numbers”,这样当我们稍后处理这个表单的时候可以找到它,表单的action属性被设置为”/”,当用户点击Calculate按钮时可以重新向该页面发送请求。这意味着在所有情况下homePage()函数总会被调用,所以在刚开始时它就必须处理没有数据输入的情况,接下来要处理有数据输入的情况或者发生错误的情况。实际上,所有的工作都是通过自定义函数processRequest()来处理的,所以该函数对每一种情况都做了相应的处理。

经过解析,我们将pageTop (代码未给出) 和form字符串常量返回给客户端。如果表单解析失败我们就返回一条错误消息;anError 是一个格式化字符串,err要被格式化为error值。

如果解析成功 (理应如此),我们就调用自定义的processRequest()函数来获取用户输入的数据并进行处理。如果这些数据有效我们就调用之前提到过的getStats() 函数来计算统计结果并将格式化后的结果返回给客户端;否则我们就返回一个错误消息。(当表单第一次显示时不需要处理数据,当然也就不会发生错误,在这种情况下ok的值为false,而message的值为空。)在函数结尾我们将仅包含</body>和</html>标签的字符串常量pageBottom的值返回给客户端(代码未给出)。

该函数从request里读取表单的数据。如果表单是第一次被请求,那么“numbers”输入框是空的,这并不是一个错误,所以我们返回一个空的float64切片、一个空的错误消息和一个false值以表明我们没有获取到任何统计结果,并显示一个空的表单。如果用户输入一些数据,我们就返回一个float64的切片、一个空的错误消息和一个true值;如果用户输入的数据无效,则返回一个可能为空的切片、一个错误消息和一个false值。

request里有一个map[string)[]string类型的Form成员。这意味着它的键是一个字符串,值是一个字符串切片。因此任何一个键都可能有任意数量的字符串作为其值。例如:如果用户输入了数字“58.27136”,那么在表单中会生成一个键为”numbers”,值为[]string{“58.27136”}的map类型,也就是说它的值是一个字符串切片且该切片仅有一个字符串。(作为对比,这里有一个包含两个字符串的切片:[]string{“123″,”abc”})。我们先检查键”numbers”是否存在(应该必须存在),如果存在且它的值至少有一个字符串,那么我们就知道可以读取并处理它了。

我们使用strings.Replace()函数来获得用户输入并将所有的逗号替换为空格。(第三个参数是执行替换的次数,-1表示替换所有的。)对于已获得的由空格分隔的字符串,我们再使用strings.Fields()函数将其(在所有的空格处)切分成一个字符串切片并使用for…range循环来遍历它。对于每一个字符串(如”5”,”8.2”)我们使用strconv.parseFloat()函数将其转换成float64类型,该函数需要传入要解析的字符串和一个位大小如32或64。如果转换失败我们立即返回己经转转换好了的float64切片、一个非空的错误信息和一个false值。如果转换成功我们将其追加到numbers切片。内置的append()函数接受一个切片和一个或多个值,并将值追加到原始切片后返回新的切片,如果原来的切片的容量大于它的长度,该函数足够智能并可以重新使用原始切片,所以该函数是非常高效的。

如果程序没有错误(无效数据),我们就返回numbers切片,一个空的错误信息和一个true值,如果没有数据需要处理(因为该表单已经被第一次请求过),我们就返回false值。

一旦统计结果被计算出,我们就必须将其返回给用户,因为该程序是一个web应用,所以我们需要生成HTML格式的内容。(Go语言的标准库有专门的text/template和html/template包用于创建数据驱动text和HTML,但是这里我们的需求是如此简单,所以我们选择自己手动写。稍后会有一个简单的基于text/template包的例子。)

fmt.Sprintf()函数接受一个格式化字符串和一个或多个值,按照格式化动作(如,%v, %d, %f),使用相应的值进行替换,并返回一个格式化后的字符串。我们并不需要做任何 HTML 转义,因为我们处理的所有值都是数字。(如果需要转义,我们可以使用template.HTMLEscape()或html.EscapeString()函数。)

如本示例所示,Go可以很容易地创建一个简单的web应用——如果我们了解一些基本的 HTML知识的话——而Go提供的html、net/http、html/ template和text/template包可以让你的编程生活更轻松。


目录


作者:Johnson
原创文章,版权所有,转载请保留原文链接。

发表评论

电子邮件地址不会被公开。 必填项已用*标注