go http 包练习

参见:

下载包 设置代理

妈的,下载包有时能下,有时候就卡住。

手动安装包

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, "

Hello, %q

", html.EscapeString(r.URL.Path)) }) http.ListenAndServe(":8080", nil) } func name_haddler(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "

hello yxl

") }
  • 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, "

Hello, %q

", html.EscapeString(r.URL.Path)) }) http.ListenAndServe(":8080", nil) } func name_haddler(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "

hello yxl

") }
  • 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

一个接口

定义源码如下:

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))
}

其他的暂时就不讲了,太难了,老铁

完整总结的代码(其实和上面一样的):

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, "

Hello, %q

", html.EscapeString(r.URL.Path)) }) http.ListenAndServe(":8080", nil) } func name_haddler(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "

hello yxl

") } // 事例 // 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知识