golang中的 net/http 包的使用详解

添加路由与处理程序
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、HandlerFunc
2*redirectHandler
3*ServeMux
4、serverHandler
5*timeoutHandler
6、globalOptionsHandler
7、initALPNRequest
8*fileHandler
文件系统服务器filesystem server

就是通过这个服务器可以访问指定目录下的文件和文件夹,任何文件都会以文本的形式将文件内容响应出去。

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)

web server
http.Handle 方法参数需要实现了 Handler 接口的类型。
http.HandleFunc 方法参数需要类型为 func(ResponseWriter, *Request)) 的变量。 

很明显,HandlerFunc 类型既能作为 http.Handle 的参数,也能作为 http.HandleFunc 的参数。

于是可以这样:

var f http.HandlerFunc
f = func(resp http.ResponseWriter, req *http.Request){
	resp.Write([]byte("haha"))
}

http.HandleFunc("/", f)
http.Handle("/h", f)
服务监听与Accept

相关函数

func ListenAndServe(addr string, handler Handler) error
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error
func Serve(l net.Listener, handler Handler) error
func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error
func 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) error
func 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() error
func (srv *Server) ListenAndServe() error
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error
func (srv *Server) RegisterOnShutdown(f func())
func (srv *Server) Serve(l net.Listener) error
func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error
func (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
返回状态码对应的描述。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值