返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

handler

发布于 2024-10-12 19:15:52 字数 6523 浏览 0 评论 0 收藏 0

处理器方法的两个参数,分别对应返回和请求数据。

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
  • Request :客户端发送的请求信息。
  • ResponseWriter :返回给客户端的数据。
  • 处理器不应修改请求数据。
  • 处理器结束后,会进行资源清理,不应再持有或访问这两个参数。
  • 返回调用次序: Header.SetWriteHeaderWrite
  • 善用 ErrorRedirectSetCookieParseTime 函数。
type TestHandler struct{}

func (*TestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// request
	fmt.Println("method:", req.Method)

	fmt.Println("   url:", req.URL)
	fmt.Println("       ", req.URL.Query())

	fmt.Println("header:")
	for k, v := range req.Header { 
		fmt.Println("       ", k, v) 
	}

	fmt.Println("  body:")
	data, _ := io.ReadAll(req.Body)
	fmt.Println("       ", string(data))

	// response
	w.Header().Set("test", "demo server")
	w.WriteHeader(http.StatusCreated)
	io.WriteString(w, "hello, world!")

}

func main() {
	log.Fatalln(http.ListenAndServe(":8080", new(TestHandler)))
}

参数

对连接和请求数据进行处理,包装成 Requestresponse 对象。
请求结束时关闭和清理资源,所以不该继续持有或访问。

// http/server.go

func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
    req, err := readRequest(c.bufr)
    w = &response{
		req:           req,
		reqBody:       req.Body,
    }
    return w, nil
}
// http/server.go

func (w *response) finishRequest() {
	w.cw.close()    
	w.reqBody.Close()
}

response.Write 内检查并调用 WriteHeader ,而后者不允许重复设定。
所以,要返回非 200 状态,须在 Write 之前调用。

// http/server.go

func (w *response) write(int, []byte, string) (n int, err error) {
	if !w.wroteHeader {
		w.WriteHeader(StatusOK)
	}
}

func (w *response) WriteHeader(code int) {
    if w.wroteHeader {
		w.conn.server.logf("http: superfluous ...", ...)
		return
	}

	w.wroteHeader = true
	w.status = code
}

路由

自带的 ServeMux 路由只能算作一个范例,功能很弱,正式项目中很少使用。
其作用是统一接收请求,然后按路径模式分发给其他已注册处理器。

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(w, "hello, world!")
	})

	srv := &http.Server{
		Addr:         ":8080",
		Handler:      mux,
		ReadTimeout:  time.Second * 10,
		WriteTimeout: time.Second * 10,
	}

	log.Fatalln(srv.ListenAndServe())
}
  • Handle :注册处理器。
  • HandleFunc :将函数包装成处理器,并注册。
  • Handler :根据请求(request)返回对应的注册处理器。
// http/server.go

type ServeMux struct {
	m   map[string]muxEntry
	es  []muxEntry // slice of entries sorted from longest to shortest.
}
// http/server.go

func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

    // 不能重复注册。
	if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

    // 创建注册字典。
	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
    
    // 存入字典。
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
    
    // 对 / 结尾的维持一个有序排列。
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}
}

算法实现太过简单,长路径模式优先。
如果没有任何匹配模式,则以 NotFoundHandler 处理。

// http/server.go

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {

    // 找出与请求匹配的处理器。
	h, _ := mux.Handler(r)
    
	h.ServeHTTP(w, r)
}
// http/server.go

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
	return mux.handler(host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
	if h == nil {
		h, pattern = mux.match(path)
	}
	if h == nil {
		h, pattern = NotFoundHandler(), ""
	}
	return
}

func (mux *ServeMux) match(path string) (h Handler, pattern string) {

    // 精确匹配。
	v, ok := mux.m[path]
	if ok {
		return v.h, v.pattern
	}

	// 模糊匹配,前缀搜索。(从长到短排列)
	for _, e := range mux.es {
		if strings.HasPrefix(path, e.pattern) {
			return e.h, e.pattern
		}
	}
	return nil, ""
}

未设置 Server.Handler ,则默认使用 DefaultServeMux 处理。

// http/server.go

var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

func Handle(pattern string, handler Handler) { 
    DefaultServeMux.Handle(pattern, handler) 
}

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

处理器

标准库实现的常用处理器:

  • NotFound, NotFoundHandler :没找到。(404)
  • Redirect, RedirectHandler :重定向。(301, 302, 303)
  • TimeoutHandler :包装其他处理器,设置超时。
  • StripPrefix :包装其他处理器,移除请求路径前缀。
  • FileServer :静态资源。(文件)
  • httputil/ReverseProxy :反向代理。

利用 HandlerFunc 将函数转型即可实现处理器,简化编码。

// http/server.go

type HandlerFunc func(ResponseWriter, *Request)  // 不是 HandleFunc !!!

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}


还可结合闭包绑定状态,例如以 channel 实现 semaphore ,控制最大并发连接。

func connLimiter(h http.Handler, max int) http.Handler {
	sema := make(chan struct{}, max)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		sema <- struct{}{}
		defer func() { <-sema }()
		h.ServeHTTP(w, r)
	})
}

测试

借助 httptest 可非常方便测试处理器。

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
)

func hello(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "hello, world!")
}

func main() {
    
    // 直接创建 Request, Response 对 Handler 进行测试。
    
	req := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	w := httptest.NewRecorder()

	hello(w, req)
	resp := w.Result()

	b, _ := io.ReadAll(resp.Body)
	fmt.Println(string(b))
}
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
)

func hello(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, "hello, world!")
}

func main() {
    
    // 使用随机端口启用 h2 服务器和客户端通信。
    // 可完成两端结合测试。
    
	s := httptest.NewUnstartedServer(http.HandlerFunc(hello))
	s.EnableHTTP2 = true
	s.StartTLS()	
	defer s.Close()

	resp, _ := s.Client().Get(s.URL)
	b, _ := io.ReadAll(resp.Body)
	resp.Body.Close()

	fmt.Println(resp)
	fmt.Println(string(b))
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文