go http 包練習
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知識