Http 请求是一种无状态的通讯模型,无状态可能对于初学者听起来有点 confusing。这里的无状态表明每一次请求都是独立相互之间没有关系。每一次请求不会受到上一次请求的影响。
在 net/http 模块中提供两个组件来处理 Http 请求。
ServeMux
ServeMux 是一个多路复用器(或简单的HTTP请求路由器),根据 HTTP 的请求来在
预定义的 URI 资源列表中查找来相应的资源返回给客户端。
我们在项目目录下创建一个 public 文件夹,文件夹中我们可以先简单地创建一个 about.html
创建一个静态的服务
package main
import(
"net/http"
)
func main() {
mux := http.NewServeMux()
fs := http.FileServer(http.Dir("public"))
mux.Handle("/", fs)
http.ListenAndServe(":8080", mux)
}
这里我们首先通过 http.NewServeMux
创建一个 ServeMux 服务。
然后用 FileServer 创建一个访问 public 文件夹下内容的处理器。
mux.Handle("/", fs)
将我们路由/
使用对应 fs 处理器来进行处理。
最后将我们的文件服务定义在 8080 端口上。
ListenAndServe
函数监听 TCP 网络地址,然后调用定义好了处理器的服务来处理来自该地址的的请求。ListenAndServe
的第二个参数是一个http.handler,这里传入了一个servemux 对象作为对端口的处理。ServeMux 也具有 serveHttp 方法,所以也满足 http.handler 接口,所以可以传递 ServeMux 对象作为 ListenAndServe 函数的第二个参数。其实 ServeMux 是 HTTP.handler 接口的实现。
[http://localhost:8080/about.html](http://localhost:8080/about.html)
自定义处理器
当然可以根据自己需要对每个路由进行不同处理,现在我们就通过示例来给大家演示如何自定义对路由进行处理的处理器。
首先我们来定义一个消息的结构体,我们需要根据用户输入不同的路由来反馈不同的内容。
type messageHandler struct{
message string
}
如果让我们消息体来对路由进行处理就需要实现 ServeHTTP 这个接口ServeHTTP(w http.ResponseWriter, r *http.Request)
,这个接口两个参数想必有过网络开发经验的人应该不会陌生,特别是熟悉 nodejs 人应该再熟悉不过了。
func (m *messageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, m.message)
}
这样我们就可以通过fmt.Fprintf(w, m.message)
这一行代码将我们消息作为字符串返回给客户端。
完整代码如下
package main
import(
"net/http"
"fmt"
"log"
)
type messageHandler struct{
message string
}
func (m *messageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, m.message)
}
func main() {
mux := http.NewServeMux()
welcomeHandler := &messageHandler{
"welcome to zidea",
}
mux.Handle("/welcome",welcomeHandler)
angularTutHandler := &messageHandler{
"angular basic tut",
}
mux.Handle("/angular",angularTutHandler)
log.Println("Listening...")
http.ListenAndServe(":8080",mux)
}
函数处理器
大家可能发现我们在 nodejs 和 python 的 flask 框架中都是通过传入一个函数来处理路由,难道在 go 语言里我们还需要先定义一个结构体,然后让结构体实现函数来实现吗?
可以通过将函数转换为HandlerFunc
func welcomeHandlerFunc(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "welcome to zidea")
}
func main() {
mux := http.NewServeMux()
welcomeHandler := http.HandlerFunc(welcomeHandlerFunc)
mux.Handle("/welcome",welcomeHandler)
log.Println("Listening...")
http.ListenAndServe(":8080",mux)
}
当函数的参数为(http.responsewriter,http.request)。handlerFunc 可以作为适配器,将这个函数用转换为 HTTP 处理程序。原因是 handlerFunc 类型中有内置方法ServeHTTP(http.responsewriter,http.request),因此就满了 足http.handler 接口,可以作为 HTTP 处理程序工作。
我们进一步优化,我们现在每一个路由对应路由处理器都是根据路由返回消息,可以看出他们是有共同点,尽然有共同点我们就可以进行整合,整合为一个函数,在 go 语言中是支持闭包的,定义一个函数 messageHandler
接受一个消息,然后返回一个 http.Handler
接口,内部我们将一个闭包传入到 http.HandlerFunc
。
func messageHandler(message string) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, message)
})
}
func main() {
mux := http.NewServeMux()
mux.Handle("/welcome",messageHandler("welcome to zidea"))
mux.Handle("/angular",messageHandler("angular basic tut"))
log.Println("Listening...")
http.ListenAndServe(":8080",mux)
}
自定义服务器
在前面的示例中,使用了http.ListenAndServe
来运行 HTTP 服务,使用默认 HTTP 服务配置,如果我们想要自定义 HTTP 服务器配置。HTTP 包提供一个名为 Server 的结构,通过 Server 我们就能够自定义 HTTP 服务器的配置了。
type Server struct {
Addr string
Handler Handler
ReadTimeout time.Duration
WriteTimeout time.Duration
MaxHeaderBytes int
TLSConfig *tls.Config
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
}
import(
"net/http"
"fmt"
"log"
"time"
)
func messageHandler(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "welcome to zidea")
}
func main() {
http.HandleFunc("/welcome", messageHandler)
server := &http.Server{
Addr: ":8080",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Println("Listening...")
server.ListenAndServe()
}
参考了 Web Development with Go 一书