合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
# 15.2 一个简单的web服务器 Http是一个比tcp更高级的协议,它描述了客户端浏览器如何与网页服务器进行通信。Go有自己的`net/http`包,我们来看看它。我们从一些简单的示例开始, 首先编写一个“Hello world!”:[查看示例15.6](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/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](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/15.4.md))。还有一种方法就是先执行`request.ParseForm()`然后再获取`request.Form\["var1"\]的第一个返回参数,就像这样: ``` 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://localhost:8080%EF%BC%89%E8%A2%AB%E8%AF%B7%E6%B1%82%E7%9A%84%E6%97%B6%E5%80%99%EF%BC%88%E6%88%96%E8%80%85%E8%BF%99%E4%B8%AA%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84%E5%85%B6%E4%BB%96%E5%9C%B0%E5%9D%80%EF%BC%89%EF%BC%8C%60HelloServer%60%E5%87%BD%E6%95%B0%E5%B0%B1%E8%A2%AB%E6%89%A7%E8%A1%8C%E4%BA%86%E3%80%82%E8%BF%99%E4%B8%AA%E5%87%BD%E6%95%B0%E6%98%AF%60http.HandlerFunc%60%E7%B1%BB%E5%9E%8B%E7%9A%84%EF%BC%8C%E5%AE%83%E4%BB%AC%E9%80%9A%E5%B8%B8%E7%94%A8%E4%BD%BF%E7%94%A8Prehandler%E6%9D%A5%E5%91%BD%E5%90%8D%EF%BC%8C%E5%9C%A8%E5%89%8D%E8%BE%B9%E5%8A%A0%E4%BA%86%E4%B8%80%E4%B8%AAPref%E5%89%8D%E7%BC%80%E3%80%82) `http.HandleFunc`注册了一个处理函数(这里是`HelloServer`)来处理对应`/`的请求。 `/`可以被替换为其他特定的url比如`/create`,`/edit`等等;你可以为每一个特定的url定义一个单独的处理函数。这个函数需要两个参数:第一个是`ReponseWriter`类型的`w`;第二个是请求`req`。程序向`w`写入了`Hello`和`r.URL.Path[1:]`组成的字符串后边的`[1:]`表示“创建一个从第一个字符到结尾的子切片”,用来丢弃掉路径开头的“/”,`fmt.Fprintf()`函数完成了本次写入(请看[章节12.8](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/12.8.md));另外一种写法是`io.WriteString(w, "hello, world!\n")` 总结:第一个参数是请求的路径,第二个参数是处理这个路径请求的函数的引用。 示例 15.6 [hello\_world\_webserver.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/examples/chapter_15/hello_world_webserver.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)前两行(没有错误处理代码)可以替换成以下写法: ``` http.ListenAndServe(":8080", http.HandlerFunc(HelloServer)) ``` 2)`fmt.Fprint`和`fmt.Fprintf`都是用来写入`http.ResponseWriter`的不错的函数(他们实现了`io.Writer`)。 比如我们可以使用 ``` fmt.Fprintf(w, "<h1>%s<h1><div>%s</div>", title, body) ``` 来构建一个非常简单的网页并插入`title`和`body`的值 如果你需要更多复杂的替换,使用模板包(请看[章节15.7](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/15.7.md)) 3)如果你需要使用安全的https连接,使用`http.ListenAndServeTLS()`代替`http.ListenAndServe()` 4)`http.HandleFunc("/", Hfunc)`中的`HFunc`是一个处理函数,如下: ``` func HFunc(w http.ResponseWriter, req *http.Request) { ... } ``` 也可以使用这种方式:`http.Handle("/", http.HandlerFunc(HFunc))` 上边的`HandlerFunc`只是一个类型名称,它定义如下: ``` type HandlerFunc func(ResponseWriter, *Request) ``` 它是一个可以把普通的函数当做HTPP处理器的适配器。如果`f`函数声明的合适,`HandlerFunc(f)`就是一个执行了`f`函数的处理器对象。 `http.Handle`的第二个参数也可以是`T`的一个obj对象:`http.Handle("/", obj)`给T提供了`SereHTTP`方法,实现了http的`Handler`接口: ``` func (obj *Typ) ServeHTTP(w http.ResponseWriter, req *http.Request) { ... } ``` 这个用法在[章节15.8](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/15.8.md)类型`Counter`和`Chan`上使用过。只要实现了`http.Handler`,`http`包就可以处理任何HTTP请求。 练习 15.2:[webhello2.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/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](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/exercises/chapter_15/hello_server.go) 创建一个空结构`hello`并使它实现`http.Handler`。运行并测试。