Files
the-way-to-go_ZH_CN/eBook/15.2.md
2017-05-29 20:10:42 -04:00

112 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 15.2 一个简单的网页服务器
Http是一个比tcp更高级的协议它描述了客户端浏览器如何与网页服务器进行通信。Go有自己的`net/http`包,我们来看看它。我们从一些简单的示例开始,
首先编写一个“Hello world!”:[查看示例15.6](examples/chapter_15/hello_world_webserver.go)
我们引入了`http`包并启动了网页服务器和15.1的`net.Listen("tcp", "localhost:50000")`函数的tcp服务器是类似的使用`http.ListenAndServe("localhost:8080", nil)`函数如果成功会返回空否则会返回一个错误可以指定localhost为其他地址8080是指定的端口号
`http.URL`描述了web服务器的地址内含存放了url字符串的`Path`属性;`http.Request`描述了客户端请求,内含一个`URL`属性
如果`req`请求是一个POST类型的html表单“var1”就是html表单中一个输入属性的名称然后用户输入的值就可以通过GO代码`req.FormValue("var1")`获取到(请看[章节15.4](15.4.md))。还有一种方法就是先执行`request.ParseForm()`然后再获取`request.Form["var1"]的第一个返回参数,就像这样:
```go
var1, found := request.Form["var1"]
```
第二个参数`found`就是`true`,如果`var1`并未出现在表单中,`found`就是`false`
表单属性实际上是一个`map[string][]string`类型。网页服务器返回了一个`http.Response`,它是通过`http.ResponseWriter`对象输出的这个对象整合了HTTP服务器的返回结果通过对它写入内容我们就讲数据发送给了HTTP客户端。
现在我们还需要编写网页服务器必须执行的程序,它是如何处理请求的呢。这是在`http.HandleFunc`函数中完成的,就是在这个例子中当根路径“/”url地址是http://localhost:8080被请求的时候或者这个服务器上的其他地址`HelloServer`函数就被执行了。这个函数是`http.HandlerFunc`类型的它们通常用使用Prehandler来命名在前边加了一个Pref前缀。
`http.HandleFunc`注册了一个处理函数(这里是`HelloServer`)来处理对应`/`的请求。
`/`可以被替换为其他特定的url比如`/create``/edit`等等你可以为每一个特定的url定义一个单独的处理函数。这个函数需要两个参数第一个是`ReponseWriter`类型的`w`;第二个是请求`req`。程序向`w`写入了`Hello`和`r.URL.Path[1:]`组成的字符串后边的`[1:]`表示“创建一个从第一个字符到结尾的子切片”,用来丢弃掉路径开头的“/”,`fmt.Fprintf()`函数完成了本次写入(请看[章节12.8](12.8.md));另外一种写法是`io.WriteString(w, "hello, world!\n")`
总结:第一个参数是请求的路径,第二个参数是处理这个路径请求的函数的引用。
示例 15.6 [hello_world_webserver.go](examples/chapter_15/hello_world_webserver.go)
```go
package main
import (
"fmt"
"log"
"net/http"
)
func HelloServer(w http.ResponseWriter, req *http.Request) {
fmt.Println("Inside HelloServer handler")
fmt.Fprintf(w, "Hello,"+req.URL.Path[1:])
}
func main() {
http.HandleFunc("/", HelloServer)
err := http.ListenAndServe("localhost:8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err.Error())
}
}
```
使用命令行启动程序,会打开一个命令窗口显示如下文字:
```
Starting Process E:/Go/GoBoek/code_examples/chapter_14/hello_world_webserver.exe
...
```
然后打开你的浏览器并输入url地址`http://localhost:8080/world`,浏览器就会出现文字:`Hello, world`,网页服务器会响应你在`:8080/`后边输入的内容
使用`fmt.Println`在控制台打印状态在每个handler被请求的时候在他们内部打印日志会很有帮助
注:
1前两行没有错误处理代码可以替换成以下写法
```go
http.ListenAndServe(":8080", http.HandlerFunc(HelloServer))
```
2`fmt.Fprint`和`fmt.Fprintf`都是用来写入`http.ResponseWriter`的不错的函数(他们实现了`io.Writer`)。
比如我们可以使用
```go
fmt.Fprintf(w, "<h1>%s<h1><div>%s</div>", title, body)
```
来构建一个非常简单的网页并插入`title`和`body`的值
如果你需要更多复杂的替换,使用模板包(请看[章节15.7](15.7.md)
3如果你需要使用安全的https连接使用`http.ListenAndServeTLS()`代替`http.ListenAndServe()`
4`http.HandleFunc("/", Hfunc)`中的`HFunc`是一个处理函数,如下:
```go
func HFunc(w http.ResponseWriter, req *http.Request) {
...
}
```
也可以使用这种方式:`http.Handle("/", http.HandlerFunc(HFunc))`
上边的`HandlerFunc`只是一个类型名称,它定义如下:
```go
type HandlerFunc func(ResponseWriter, *Request)
```
它是一个可以把普通的函数当做HTTP处理器的适配器。如果`f`函数声明的合适,`HandlerFunc(f)`就是一个执行了`f`函数的处理器对象。
`http.Handle`的第二个参数也可以是`T`的一个obj对象`http.Handle("/", obj)`给T提供了`ServeHTTP`方法实现了http的`Handler`接口:
```go
func (obj *Typ) ServeHTTP(w http.ResponseWriter, req *http.Request) {
...
}
```
这个用法在[章节15.8](15.8.md)类型`Counter`和`Chan`上使用过。只要实现了`http.Handler``http`包就可以处理任何HTTP请求。
练习 15.2[webhello2.go](exercises/chapter_15/webhello2.go)
编写一个网页服务器监听端口9999有如下处理函数
* 当请求`http://localhost:9999/hello/Name`时,响应:`hello Name`(Name需是一个合法的姓比如Chris或者Madeleine)
* 当请求`http://localhost:9999/shouthello/Name`时,响应:`hello NAME`
练习 15.3[hello_server.go](exercises/chapter_15/hello_server.go)
创建一个空结构`hello`并使它实现`http.Handler`。运行并测试。
## 链接
- [目录](directory.md)
- 上一章:[tcp服务器](15.1.md)
- 下一节:[访问并读取页面数据](15.3.md)