go http 包练习
source link: https://studygolang.com/articles/29829
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
https://golang.org/pkg/net/http/
几个官方的例子,拿来练习一下,wc, 体会到了 go http 包的强大之处:
下面是一些例子,来主要讲解下面的几个函数
FileServer
FileServer (DotFileHiding)
FileServer (StripPrefix)
Get
Handle
HandleFunc
Hijacker
ListenAndServe
ListenAndServeTLS
NotFoundHandler
ResponseWriter (Trailers)
ServeMux.Handle
Server.Shutdown
主体结构是:
func main(){ FileServer() ... }
我把每个函数的例子,封装为一个函数 然后放在 main 函数里面执行一下。main 都是重复的,所以就不贴代码了。(联系 采用 subline,炒鸡方便)
-
FileServer
func FileServer(root FileSystem) Handler
这个函数也是很牛逼, 主要是可以把文件路径映射为 url 路由。
https://golang.org/pkg/net/http/#FileServer
实现原理:
https://shockerli.net/post/golang-pkg-http-file-server/使用 FileSystem 接口 root 提供文件访问服务的 HTTP 处理器。可以方便的实现静态文件服务器
func FileServer(){ http.ListenAndServe(":8081", http.FileServer(http.Dir("F:/go/"))) }
运行效果:
注意多次运行,可能需要更改端口号
点击 go_practice 链接(文件夹)
点击 http_pkg.go
上面是个简单的 可以展示文件目录的功能,go 底层,帮我们实现了 文件读取和展示。
支持子目录路径
只是比上面多了个 自定义路由前缀
http.StripPrefix() 方法配合 http.Handle() 或 http.HandleFunc() 可以实现带路由前缀的文件服务。
func FileServer(){ // // http.ListenAndServe(":8081", http.FileServer(http.Dir("F:/go/"))) http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("F:/go/")))) http.ListenAndServe(":8082", nil) }
运行效果:
会比第一个程序多一个 tmpfiles 路径前缀
-
Handle
func Handle(pattern string, handler Handler)
路由匹配处理函数, 要实现 Handler 接口的 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法
package main import ( "fmt" "log" "net/http" "sync" ) type countHandler struct { mu sync.Mutex // guards n n int } func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.mu.Lock() defer h.mu.Unlock() h.n++ fmt.Fprintf(w, "count is %d\n", h.n) } func main() { http.Handle("/count", new(countHandler)) log.Fatal(http.ListenAndServe(":8080", nil)) }
-
HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
和 Handle 几乎一样,唯一不同的是参数不一样,这个是自己定义一个
xx(w http.ResponseWriter, r *http.Request) 这样的函数,儿不必叫 ServeHTTP 名字,因为 HandleFunc 函数第二个参数是 直接一个函数类型,二不是 有ServeHTTP 函数的 Handler 接口类型
func simple_server(){ http.HandleFunc("/my_name", name_haddler) http.HandleFunc("/url", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello, %q</h1>", html.EscapeString(r.URL.Path)) }) http.ListenAndServe(":8080", nil) } func name_haddler(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "<h1>hello yxl</h1>") }
-
ListenAndServe
func ListenAndServe(addr string, handler Handler) error
第二个参数通常为 nil, 在这种情况下,使用DefaultServeMux。
func simple_server(){ http.HandleFunc("/my_name", name_haddler) http.HandleFunc("/url", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello, %q</h1>", html.EscapeString(r.URL.Path)) }) http.ListenAndServe(":8080", nil) } func name_haddler(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "<h1>hello yxl</h1>") }
-
ListenAndServeTLS
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error
ListenAndServe HTTPS 版本
-
NotFoundHandler
404 对应的处理
func NotFoundHandler() Handler
func NotFoundHandler(){ // 404 对应的处理 mux.Handle("/resources", http.NotFoundHandler()) }
-
ResponseWriter
一个接口
定义源码如下:
<pre style="font-family: Menlo, monospace; font-size: 0.875rem; line-height: 1.4; overflow-x: auto; margin: 1.25rem; background: rgb(239, 239, 239); padding: 0.625rem; border-radius: 0.3125rem; color: rgb(62, 64, 66); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">type ResponseWriter interface { // Header returns the header map that will be sent by // WriteHeader. The Header map also is the mechanism with which // Handlers can set HTTP trailers. // // Changing the header map after a call to WriteHeader (or // Write) has no effect unless the modified headers are // trailers. // // There are two ways to set Trailers. The preferred way is to // predeclare in the headers which trailers you will later // send by setting the "Trailer" header to the names of the // trailer keys which will come later. In this case, those // keys of the Header map are treated as if they were // trailers. See the example. The second way, for trailer // keys not known to the Handler until after the first Write, // is to prefix the Header map keys with the TrailerPrefix // constant value. See TrailerPrefix. // // To suppress automatic response headers (such as "Date"), set // their value to nil. Header() [Header](https://golang.org/pkg/net/http/#Header) // Write writes the data to the connection as part of an HTTP reply. // // If WriteHeader has not yet been called, Write calls // WriteHeader(http.StatusOK) before writing the data. If the Header // does not contain a Content-Type line, Write adds a Content-Type set // to the result of passing the initial 512 bytes of written data to // DetectContentType. Additionally, if the total size of all written // data is under a few KB and there are no Flush calls, the // Content-Length header is added automatically. // // Depending on the HTTP protocol version and the client, calling // Write or WriteHeader may prevent future reads on the // Request.Body. For HTTP/1.x requests, handlers should read any // needed request body data before writing the response. Once the // headers have been flushed (due to either an explicit Flusher.Flush // call or writing enough data to trigger a flush), the request body // may be unavailable. For HTTP/2 requests, the Go HTTP server permits // handlers to continue to read the request body while concurrently // writing the response. However, such behavior may not be supported // by all HTTP/2 clients. Handlers should read before writing if // possible to maximize compatibility. Write([][byte](https://golang.org/pkg/builtin/#byte)) ([int](https://golang.org/pkg/builtin/#int), [error](https://golang.org/pkg/builtin/#error)) // WriteHeader sends an HTTP response header with the provided // status code. // // If WriteHeader is not called explicitly, the first call to Write // will trigger an implicit WriteHeader(http.StatusOK). // Thus explicit calls to WriteHeader are mainly used to // send error codes. // // The provided code must be a valid HTTP 1xx-5xx status code. // Only one header may be written. Go does not currently // support sending user-defined 1xx informational headers, // with the exception of 100-continue response header that the // Server sends automatically when the Request.Body is read. WriteHeader(statusCode [int](https://golang.org/pkg/builtin/#int)) }</pre>
其他的暂时就不讲了,太难了,老铁
完整总结的代码(其实和上面一样的):
package main /* http 包的练习 */ import ( "fmt" "net/http" "io/ioutil" "html" _ "log" ) func easy_test(url string){ resp, err := http.Get(url) if err != nil{ fmt.Printf("%v", err) return } defer resp.Body.Close() // 入站是当前内存区域的 执行, 和主函数分开的,每个块内存都可以有自己的 压栈 defer fmt.Printf("请求函数执行结束") body, _ := ioutil.ReadAll(resp.Body) fmt.Printf("%s", body) return } func simple_server(){ http.HandleFunc("/my_name", name_haddler) http.HandleFunc("/url", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello, %q</h1>", html.EscapeString(r.URL.Path)) }) http.ListenAndServe(":8080", nil) } func name_haddler(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "<h1>hello yxl</h1>") } // 事例 // FileServer // FileServer (DotFileHiding) // FileServer (StripPrefix) // Get // Handle // HandleFunc // Hijacker // ListenAndServe // ListenAndServeTLS // NotFoundHandler // ResponseWriter (Trailers) // ServeMux.Handle // Server.Shutdown // StripPrefix // FileServer func FileServer(root FileSystem) Handler func FileServer(){ http.ListenAndServe(":8081", http.FileServer(http.Dir("F:/go/"))) } // FileServer (DotFileHiding) func FileServer_DotFileHiding(){ // 这个看不懂啊 } // FileServer (StripPrefix) func FileServer_StripPrefix(){ // 这个就是子在 FileServer 基础上加了一个前缀路由地址 http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("F:/go/")))) http.ListenAndServe(":8082", nil) } // Get func Get(url string) (resp *Response, err error) func get(){ // 请求一个地址, 如果状态码是 30x x 是12378之一 就自动重定向,最多 10 次 // 参见 11 行 easy_test } // Handle func Handle(pattern string, handler Handler) func handle(){ // 就是添加路由,匹配对应一个函数酱紫 // 这个就暂时补贴代码了 // 路由匹配处理函数(结构体类型), 要实现 Handler 接口的 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法 } // HandleFunc func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) func HandleFunc(){ // 和 Handle 几乎一样,唯一不同的是参数不一样,这个是自己定义一个 // xx(w http.ResponseWriter, r *http.Request) 这样的函数,儿不必叫 ServeHTTP 名字,因为 HandleFunc 函数第二个参数是 直接一个函数类型,二不是 有ServeHTTP 函数的 Handler 接口类型 // 参见 simple_server 24-37 行 } // ListenAndServe func ListenAndServe(addr string, handler Handler) error func ListenAndServe(){ // 参见 } // ListenAndServeTLS func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error func ListenAndServeTLS(){ // ListenAndServe HTTPS 版本 } // NotFoundHandler func NotFoundHandler() Handler func NotFoundHandler(){ // 404 对应的处理 mux.Handle("/resources", http.NotFoundHandler()) } // ResponseWriter interface func ResponseWriter(){ // 上面所有的 处理函数,都直接或者间接实现这个 函数了 // 如: func(w http.ResponseWriter, r *http.Request) // 还有 Handler 接口定义的 ServeHTTP(ResponseWriter, *Request) 函数(同包就省略了 http.) } // func (*ServeMux) ServeMux func (mux *ServeMux) Handle(pattern string, handler Handler) func ServeMux_ServeMux(){ // 这个不太懂 } // func (*Server) Shutdown func (srv *Server) Shutdown(ctx context.Context) error func Server_Shutdown(){ // 这个也不会 } // StripPrefix func StripPrefix(prefix string, h Handler) Handler func StripPrefix(){ // 删除请求的特定前缀 // 参见: FileServer_StripPrefix 70 行 } func main(){ // easy_test("https://sunrain.xyz") // simple_server() FileServer() }
欢迎关注我们的微信公众号,每天学习Go知识
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK