本文共 6639 字,大约阅读时间需要 22 分钟。
func Handle(pattern string, handler Handler)func HandleFunc(pattern string, handler func(ResponseWriter, *Request))type Handler interface { ServeHTTP(ResponseWriter, *Request)}type HandlerFunc func(ResponseWriter, *Request)
实现了Handler接口的类型有:
1、HandlerFunc2、*redirectHandler3、*ServeMux4、serverHandler5、*timeoutHandler6、globalOptionsHandler7、initALPNRequest8、*fileHandler
就是通过这个服务器可以访问指定目录下的文件和文件夹,任何文件都会以文本的形式将文件内容响应出去。
http.FileServer() 函数返回一个 *fileHandler ,也就是 Handler接口。func FileServer(root FileSystem) Handler { return &fileHandler{ root}}type FileSystem interface { Open(name string) (File, error)}
而 http.Dir 类型实现了 FileSystem 接口:
type Dir string
于是可以这样:
var d http.Dir = "./"http.Handle("/",http.FileServer(d))
简化一下,使用强制类型转换将字符串转换成 http.Dir 类型
http.Handle("/",http.FileServer(http.Dir("./")))
同一个 pattern 只能被注册一次,否则直接 panic ,而不是覆盖!!!
pattern 和 handler 的组合是存储在一个 map 中。
// func (mux *ServeMux) Handle(pattern string, handler Handler)
http.Handle 方法参数需要实现了 Handler 接口的类型。http.HandleFunc 方法参数需要类型为 func(ResponseWriter, *Request)) 的变量。
很明显,HandlerFunc 类型既能作为 http.Handle 的参数,也能作为 http.HandleFunc 的参数。
于是可以这样:
var f http.HandlerFuncf = func(resp http.ResponseWriter, req *http.Request){ resp.Write([]byte("haha"))}http.HandleFunc("/", f)http.Handle("/h", f)
相关函数
func ListenAndServe(addr string, handler Handler) errorfunc ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) errorfunc Serve(l net.Listener, handler Handler) errorfunc ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) errorfunc ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker)func ServeFile(w ResponseWriter, r *Request, name string)
// The handler is typically nil, in which case the DefaultServeMux is used.
DefaultServeMux 就是在通过 http.Handle 和 http.HandleFunc 设置路由的时候内部使用的对象。也就是说,如果在 ListenAndServe 这里设置了 Handler,那么将会替代掉前面的设置,实际上,在这里是提供了更加灵活的方式你去自定义路由规则,中间件等。
上面已经设置了 handler,此处不再设置。
l, err := net.Listen("tcp", ":8081")if err != nil { fmt.Println(err) return}http.Serve(l, nil)
进化版:
http.ListenAndServe(":8080",nil)
如何实现多个进程可以监听同一个端口?
一个端口只能被监听一次,所以不能多次监听,但是监听之后可以复用fd,端口监听成功后会打开一个fd,可以开启一个子进程,将此fd复制到新的进程空间,并转换成listener,通常 graceful restart 就是这么实现的。
具体参考:https://github.com/phprao/gracefulrpc/blob/master/gracefulrpc.go假设监听的 fd 为 3。
f := os.NewFile(uintptr(3), "")l, err := net.FileListener(f)if err != nil { fmt.Println(err) return}http.Serve(l, nil)
如果服务器要处理https事情,需要使用SSL/TSL。
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) errorfunc ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error
大部分时候,都会在外层来个nginx作反向代理,此时可以由nginx来做https的解析工作,最后将处理后的请求转发给你的 http web server,此时你就不需要使用 TSL 服务了。
这个 http.ServeFile() 方法是干嘛的呢?
其实它是属于文件系统的方法,此方法在 fs.go 文件里面// ServeFile replies to the request with the contents of the named// file or directory.//// If the provided file or directory name is a relative path, it is// interpreted relative to the current directory and may ascend to// parent directories. If the provided name is constructed from user// input, it should be sanitized before calling ServeFile.//// As a precaution, ServeFile will reject requests where r.URL.Path// contains a ".." path element; this protects against callers who// might unsafely use filepath.Join on r.URL.Path without sanitizing// it and then use that filepath.Join result as the name argument.//// As another special case, ServeFile redirects any request where r.URL.Path// ends in "/index.html" to the same path, without the final// "index.html". To avoid such redirects either modify the path or// use ServeContent.//// Outside of those two special cases, ServeFile does not use// r.URL.Path for selecting the file or directory to serve; only the// file or directory provided in the name argument is used.func ServeFile(w ResponseWriter, r *Request, name string)
其实文件系统在前面已经实现过了,但是 ServeFile 是一个更小粒度的实现,它的作用是返回指定的文件的内容,至于文件的路径问题,在注释中有说明,它不是一个Handler,因此可以将其写在Handler里面,例如:
http.HandleFunc("/download", func(resp http.ResponseWriter, req *http.Request){ http.ServeFile(resp, req, "out.gif")})
它会根据文件类型设置 Content-Type 。
如果需要下载,设置 header 的 Content-Disposition 属性,
http.HandleFunc("/download", func(resp http.ResponseWriter, req *http.Request){ resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", "out.gif")) http.ServeFile(resp, req, "out.gif")})
net/http 包中有个很重要的类型,Server ,整个服务都是围绕着它展开的。比如:
func ListenAndServe(addr string, handler Handler) error { server := &Server{ Addr: addr, Handler: handler} return server.ListenAndServe()}
如果想对服务做更精细的控制,可以基于Server自己封装。
func (srv *Server) Close() errorfunc (srv *Server) ListenAndServe() errorfunc (srv *Server) ListenAndServeTLS(certFile, keyFile string) errorfunc (srv *Server) RegisterOnShutdown(f func())func (srv *Server) Serve(l net.Listener) errorfunc (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) errorfunc (srv *Server) SetKeepAlivesEnabled(v bool)func (srv *Server) Shutdown(ctx context.Context) error
如果要停止服务的话使用 Shutdown(), 而不是 Close() ,因为前者实现了 graceful shut down , ctx 使用 context.Background() 。
RegisterOnShutdown() 方法允许你设置一些 func,以至于在 Shutdown() 的时候被异步调用,注意哦,并不是同步 Hook,部分源码:
func (srv *Server) RegisterOnShutdown(f func()) { srv.mu.Lock() srv.onShutdown = append(srv.onShutdown, f) srv.mu.Unlock()}func (srv *Server) Shutdown(ctx context.Context) error { ... for _, f := range srv.onShutdown { go f() } ...}
SetKeepAlivesEnabled() 设置 whether HTTP keep-alives are enabled,By default, keep-alives are always enabled
func NotFound(w ResponseWriter, r *Request)
func NotFoundHandler() Handler 内置的在解析路由的时候如果没找到则会使用此方法作为Handler,就是 404 page not found。func Error(w ResponseWriter, error string, code int)
内置的响应错误信息的方法func Redirect(w ResponseWriter, r *Request, url string, code int)
实现自定义跳转,状态码 StatusMovedPermanently, StatusFound or StatusSeeOther,其注释如下:// Redirect replies to the request with a redirect to url,// which may be a path relative to the request path.//// The provided code should be in the 3xx range and is usually// StatusMovedPermanently, StatusFound or StatusSeeOther.//// If the Content-Type header has not been set, Redirect sets it// to "text/html; charset=utf-8" and writes a small HTML body.// Setting the Content-Type header to any value, including nil,// disables that behavior.
func SetCookie(w ResponseWriter, cookie *Cookie)
设置cookie,可以设置多个 k/v 。http.SetCookie(w, &http.Cookie{ Name: "name", Value: "value"})http.SetCookie(w, &http.Cookie{ Name: "name1", Value: "value1"})
func StatusText(code int) string
返回状态码对应的描述。转载地址:http://bnaui.baihongyu.com/