分享
  1. 首页
  2. 文章

Go-HTTP

u013344915 · · 1972 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

package net/http

这个package支持:web application, web APIs;简单地说,就是可以用net/http来实现HTTP客户端和服务器的开发。 另外,还要用到package html/template。

该书作者不建议初学者一开始就用第三方库,而是先用Go发布时的net/http等标准库。

在本系列中,会略过HTTP的概念,这方面可以找专门的HTTP书籍做参考。

处理HTTP请求

如果是构建WEB APIs,那么服务器会返回XML或JSON;如果是构建WEB应用,则服务器返回HTML网页。

net/http的两个主要构件(src/net/http/server.go):
- ServeMux: 多路复用,或HTTP请求路由。讲HTTP请求和预定义的URIs进行比较,然后调用对应的Handler来处理这个HTTP请求。——对应于Tomcat容器。
- Handler: 负责写HTTP响应结果(Header&Body)。http.Handler是一个接口,用户要做的就是实现这个接口。——可以和Spring @Controller进行对比。

Handler

源码

// A Handler responds to an HTTP request.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished; it
// is not valid to use the ResponseWriter or read from the
// Request.Body after or concurrently with the completion of the
// ServeHTTP call.
//
// Depending on the HTTP client software, HTTP protocol version, and
// any intermediaries between the client and the Go server, it may not
// be possible to read from the Request.Body after writing to the
// ResponseWriter. Cautious handlers should read the Request.Body
// first, and then reply.
//
// Except for reading the body, handlers should not modify the
// provided Request.
//
// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
// that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log,
// and hangs up the connection.
type Handler interface {
 ServeHTTP(ResponseWriter, *Request)
}

要点

  • ResponseWriter对象用来构件Response,包括Header和Body。
  • 一旦往ResponseWriter中写了数据之后,就不要再访问Request对象了。
  • ServeHttp不能修改Request的值。

ResponseWriter

ResponseWriter也是一个接口,定义了3个方法。

// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
//
// A ResponseWriter may not be used after the Handler.ServeHTTP method
// has returned.
type ResponseWriter interface {
 // Header returns the header map that will be sent by
 // WriteHeader. Changing the header after a call to
 // WriteHeader (or Write) has no effect unless the modified
 // headers were declared as trailers by setting the
 // "Trailer" header before the call to WriteHeader (see example).
 // To suppress implicit response headers, set their value to nil.
 Header() 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.
 //
 // 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) (int, error)
 // WriteHeader sends an HTTP response header with 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.
 WriteHeader(int)
}

FileServer/静态页面

package main 
import (
 "net/http"
)
func main() {
 mux := http.NewServeMux()
 fs := http.FileServer(http.Dir("public"))
 mux.Handle("/", fs)
 http.ListenAndServe(":8080", mux)
}

这里需要创建一个public文件夹,这个public文件夹和.go文件(如helloworld.go)是在同一级目录;——为此只需要注意到public采用的是相对路径。

比如接下来在public目录下创建一个about.html文件,那么就可以用http://localhost:8080/about.html来访问这个页面。

这里的FileServer()定义在src/http/fs.go中,这个函数返回的是type fileHandler struct,这个struct实现了Handler接口。

自定义的Handler/Creating Custom Handlers

see 《Web Development with Go》Page 63.

用户可以创建自己的struct,只要实现了Handler接口,就可以用来作为HTTP消息处理的handler。

package main 
import (
 "fmt"
 "log"
 "net/http"
)
type MyServeMux struct {
 http.ServeMux
 id int
}
type MessageHandler struct {
 msg string 
}
func (m *MessageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, m.msg)
}
/*
http://localhost:8080/first -> handler1
http://localhost:8080/second -> handler2
http://localhost:8080/second/item1 -> handler21
http://localhost:8080/second/item2 -> 404 page not found
*/
func main() {
 mux := new (MyServeMux)
 handler1 := &MessageHandler{"The first handler."}
 handler2 := &MessageHandler{"The second handler."}
 handler21 := &MessageHandler{"The second handler, and item is 1."}
 mux.Handle("/first", handler1)
 mux.Handle("/second", handler2)
 mux.Handle("/second/item1", handler21)
 log.Println("Listening...")
 http.ListenAndServe(":8080", mux)
}

中间部分代码也可以写作:

handler1 := MessageHandler{"The first handler."}
handler2 := MessageHandler{"The second handler."}
handler21 := MessageHandler{"The second handler, and item is 1."}
mux.Handle("/first", &handler1)
mux.Handle("/second", &handler2)
mux.Handle("/second/item1", &handler21)

要点:

  • 需要通过指针/地址方式,实现多态功能。
  • ServeMux是用来维护URI和Handler之间的映射关系。
  • 每个Handler就是Spring的@Controller。

http.HandlerFunc type

前面的例子中,先创建一个struct,并为之实现ServeHttp()接口,从而将struct变成一个Handler。有时候,如果struct没有field,则可以用http.HandlerFunc类型的方法。

定义/server.go

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
 f(w, r)
}

这里的代码说明了,type不仅可以用于数据类型,也可以用于函数。

HandlerFunc是一个函数类型,主要用户自定义的函数符合这个signature,那么就可以用HandlerFunc(foo)将这个foo函数强制转换成HandlerFunc类型。又因为HandlerFunc类型实现了ServerHttp()接口,所以这个HandlerFunc对象(这又像C++的functor了)也就自动是一个Handler了。

注意到:抛开receiver,那么HandlerFunc的signature和ServerHttp是一样的。

示例

package main 
import (
 "fmt"
 "log"
 "net/http"
)
func msgHandler(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, "Hello, HandlerFunc!")
}
func main() {
 mux := http.NewServeMux()
 handler := http.HandlerFunc(msgHandler)
 mux.Handle("/hello", handler)
 log.Println("Listening...")
 http.ListenAndServe(":8080", mux)
}

有struct改成func之后,之前的参数化功能(可定制msg)消失了。为此,可以对func进行改进。

改进:函数参数化

package main 
import (
 "fmt"
 "log"
 "net/http"
)
func msgHandler(msg string) http.Handler {
 return http.HandlerFunc(
 func(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, msg)
 })
}
func main() {
 mux := http.NewServeMux()
 handler1 := msgHandler("first handler")
 handler2 := msgHandler("second handler")
 mux.Handle("/first", handler1)
 mux.Handle("/second", handler2)
 log.Println("Listening...")
 http.ListenAndServe(":8080", mux)
}

ServeMux.HandleFunc()

Go总是尽力为用户提供方便。为了阐述这一点,先给出示例:

示例一

package main 
import (
 "fmt"
 "log"
 "net/http"
)
func msgHandler(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, "Hello, HandlerFunc!")
}
func main() {
 mux := http.NewServeMux()
 //handler := http.HandlerFunc(msgHandler)
 //mux.Handle("/hello", handler)
 mux.HandleFunc("/hello", msgHandler)
 log.Println("Listening...")
 http.ListenAndServe(":8080", mux)
}

在这个例子中,把之前的两句合并为一句。为了说明其含义,给出ServeMux.HandleFunc()的定义。

ServeMux.HandleFunc()

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 mux.Handle(pattern, HandlerFunc(handler))
}

对照这个源码,不难理解两句合并为一句的原理。故无须再啰嗦。

示例二

另一个优化示例。

package main 
import (
 "fmt"
 "log"
 "net/http"
)
func msgHandler(msg string) http.HandlerFunc/*http.Handler*/ {
 return http.HandlerFunc(
 func(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, msg)
 })
}
func main() {
 mux := http.NewServeMux()
 /*handler1 := msgHandler("first handler")
 handler2 := msgHandler("second handler")
 mux.Handle("/first", handler1)
 mux.Handle("/second", handler2)*/
 mux.HandleFunc("/first", msgHandler("first handler"))
 mux.HandleFunc("/second", msgHandler("second handler"))
 log.Println("Listening...")
 http.ListenAndServe(":8080", mux)
}

或者改为:

func msgHandler2(msg string) func(http.ResponseWriter, *http.Request) {
 return func(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, msg)
 }
}
func main() {
 //...
 mux.HandleFunc("/first", msgHandler2("first handler"))
 mux.HandleFunc("/second", msgHandler2("second handler"))
 //...
}

ServeMux

NewServeMux()

该函数(src/net/http/server.go)的定义如下:

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }

也就是说,这个函数创建一个ServeMux对象,并返回该对象(的指针)。

DefaultServeMux

DefaultServeMux是http package中定义的一个ServeMux对象:

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

那么什么时候用到DefaultServeMux呢?——当用户代码没有定义ServeMux对象的时候。

示例代码:

package main 
import (
 "fmt"
 "log"
 "net/http"
)
func msgHandler(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, "Hello, HandlerFunc!")
}
func main() {
 //mux := http.NewServeMux()
 /*mux*/http.HandleFunc("/hello", msgHandler)
 log.Println("Listening...")
 http.ListenAndServe(":8080", nil/*mux*/)
}

可以看到,用DefaultServeMux会简化代码。

那么接下来,就把相关的函数的源码列出来,以此理解背后的原理。

HandleFunc

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 DefaultServeMux.HandleFunc(pattern, handler)
}
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 mux.Handle(pattern, HandlerFunc(handler))
}

ListenAndServe()

这个流程相当长,可以跳过前面,直接跑到最后一个函数。——中间函数罗列在这里,供参考。

func ListenAndServe(addr string, handler Handler) error {
 server := &Server{Addr: addr, Handler: handler}
 return server.ListenAndServe()
}
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
// If srv.Addr is blank, ":http" is used.
// ListenAndServe always returns a non-nil error.
func (srv *Server) ListenAndServe() error {
 addr := srv.Addr
 if addr == "" {
 addr = ":http"
 }
 ln, err := net.Listen("tcp", addr)
 if err != nil {
 return err
 }
 return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// For HTTP/2 support, srv.TLSConfig should be initialized to the
// provided listener's TLS Config before calling Serve. If
// srv.TLSConfig is non-nil and doesn't include the string "h2" in
// Config.NextProtos, HTTP/2 support is not enabled.
//
// Serve always returns a non-nil error.
func (srv *Server) Serve(l net.Listener) error {
 defer l.Close()
 if fn := testHookServerServe; fn != nil {
 fn(srv, l)
 }
 var tempDelay time.Duration // how long to sleep on accept failure
 if err := srv.setupHTTP2_Serve(); err != nil {
 return err
 }
 // TODO: allow changing base context? can't imagine concrete
 // use cases yet.
 baseCtx := context.Background()
 ctx := context.WithValue(baseCtx, ServerContextKey, srv)
 ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
 for {
 rw, e := l.Accept()
 if e != nil {
 if ne, ok := e.(net.Error); ok && ne.Temporary() {
 if tempDelay == 0 {
 tempDelay = 5 * time.Millisecond
 } else {
 tempDelay *= 2
 }
 if max := 1 * time.Second; tempDelay > max {
 tempDelay = max
 }
 srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
 time.Sleep(tempDelay)
 continue
 }
 return e
 }
 tempDelay = 0
 c := srv.newConn(rw)
 c.setState(c.rwc, StateNew) // before Serve can return
 go c.serve(ctx)
 }
}
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
 c.remoteAddr = c.rwc.RemoteAddr().String()
 defer func() {
 if err := recover(); err != nil {
 const size = 64 << 10
 buf := make([]byte, size)
 buf = buf[:runtime.Stack(buf, false)]
 c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
 }
 if !c.hijacked() {
 c.close()
 c.setState(c.rwc, StateClosed)
 }
 }()
 if tlsConn, ok := c.rwc.(*tls.Conn); ok {
 if d := c.server.ReadTimeout; d != 0 {
 c.rwc.SetReadDeadline(time.Now().Add(d))
 }
 if d := c.server.WriteTimeout; d != 0 {
 c.rwc.SetWriteDeadline(time.Now().Add(d))
 }
 if err := tlsConn.Handshake(); err != nil {
 c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
 return
 }
 c.tlsState = new(tls.ConnectionState)
 *c.tlsState = tlsConn.ConnectionState()
 if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
 if fn := c.server.TLSNextProto[proto]; fn != nil {
 h := initNPNRequest{tlsConn, serverHandler{c.server}}
 fn(c.server, tlsConn, h)
 }
 return
 }
 }
 // HTTP/1.x from here on.
 c.r = &connReader{r: c.rwc}
 c.bufr = newBufioReader(c.r)
 c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
 ctx, cancelCtx := context.WithCancel(ctx)
 defer cancelCtx()
 for {
 w, err := c.readRequest(ctx)
 if c.r.remain != c.server.initialReadLimitSize() {
 // If we read any bytes off the wire, we're active.
 c.setState(c.rwc, StateActive)
 }
 if err != nil {
 if err == errTooLarge {
 // Their HTTP client may or may not be
 // able to read this if we're
 // responding to them and hanging up
 // while they're still writing their
 // request. Undefined behavior.
 io.WriteString(c.rwc, "HTTP/1.1 431 Request Header Fields Too Large\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n431 Request Header Fields Too Large")
 c.closeWriteAndWait()
 return
 }
 if err == io.EOF {
 return // don't reply
 }
 if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
 return // don't reply
 }
 var publicErr string
 if v, ok := err.(badRequestError); ok {
 publicErr = ": " + string(v)
 }
 io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n400 Bad Request"+publicErr)
 return
 }
 // Expect 100 Continue support
 req := w.req
 if req.expectsContinue() {
 if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
 // Wrap the Body reader with one that replies on the connection
 req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
 }
 } else if req.Header.get("Expect") != "" {
 w.sendExpectationFailed()
 return
 }
 // HTTP cannot have multiple simultaneous active requests.[*]
 // Until the server replies to this request, it can't read another,
 // so we might as well run the handler in this goroutine.
 // [*] Not strictly true: HTTP pipelining. We could let them all process
 // in parallel even if their responses need to be serialized.
 serverHandler{c.server}.ServeHTTP(w, w.req)
 w.cancelCtx()
 if c.hijacked() {
 return
 }
 w.finishRequest()
 if !w.shouldReuseConnection() {
 if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
 c.closeWriteAndWait()
 }
 return
 }
 c.setState(c.rwc, StateIdle)
 }
}
// initNPNRequest is an HTTP handler that initializes certain
// uninitialized fields in its *Request. Such partially-initialized
// Requests come from NPN protocol handlers.
type initNPNRequest struct {
 c *tls.Conn
 h serverHandler
}
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
 srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
 handler := sh.srv.Handler
 if handler == nil {
 handler = DefaultServeMux
 }
 if req.RequestURI == "*" && req.Method == "OPTIONS" {
 handler = globalOptionsHandler{}
 }
 handler.ServeHTTP(rw, req)
}

ServeMux struct

数据成员:

type ServeMux struct {
 mu sync.RWMutex
 m map[string]muxEntry
 hosts bool // whether any patterns contain hostnames
}

其中map是URI到Handler之间的映射关系。相关的函数或成员方法如下(godoc cmd/net/http ServeMux):

type ServeMux struct {
 // contains filtered or unexported fields
}
 ServeMux is an HTTP request multiplexer. It matches the URL of each
 incoming request against a list of registered patterns and calls the
 handler for the pattern that most closely matches the URL.
 Patterns name fixed, rooted paths, like "/favicon.ico", or rooted
 subtrees, like "/images/" (note the trailing slash). Longer patterns
 take precedence over shorter ones, so that if there are handlers
 registered for both "/images/" and "/images/thumbnails/", the latter
 handler will be called for paths beginning "/images/thumbnails/" and the
 former will receive requests for any other paths in the "/images/"
 subtree.
 Note that since a pattern ending in a slash names a rooted subtree, the
 pattern "/" matches all paths not matched by other registered patterns,
 not just the URL with Path == "/".
 If a subtree has been registered and a request is received naming the
 subtree root without its trailing slash, ServeMux redirects that request
 to the subtree root (adding the trailing slash). This behavior can be
 overridden with a separate registration for the path without the
 trailing slash. For example, registering "/images/" causes ServeMux to
 redirect a request for "/images" to "/images/", unless "/images" has
 been registered separately.
 Patterns may optionally begin with a host name, restricting matches to
 URLs on that host only. Host-specific patterns take precedence over
 general patterns, so that a handler might register for the two patterns
 "/codesearch" and "codesearch.google.com/" without also taking over
 requests for "http://www.google.com/".
 ServeMux also takes care of sanitizing the URL request path, redirecting
 any request containing . or .. elements or repeated slashes to an
 equivalent, cleaner URL.
func NewServeMux() *ServeMux
 NewServeMux allocates and returns a new ServeMux.
func (mux *ServeMux) Handle(pattern string, handler Handler)
 Handle registers the handler for the given pattern. If a handler already
 exists for pattern, Handle panics.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
 HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
 Handler returns the handler to use for the given request, consulting
 r.Method, r.Host, and r.URL.Path. It always returns a non-nil handler.
 If the path is not in its canonical form, the handler will be an
 internally-generated handler that redirects to the canonical path.
 Handler also returns the registered pattern that matches the request or,
 in the case of internally-generated redirects, the pattern that will
 match after following the redirect.
 If there is no registered handler that applies to the request, Handler
 returns a ``page not found'' handler and an empty pattern.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
 ServeHTTP dispatches the request to the handler whose pattern most
 closely matches the request URL.

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:u013344915

查看原文:Go-HTTP

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1972 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏