http - MiddleWare Server.Handler

基础

  • 使用 http.Handlerhttp.HandlerFunc
1
2
3
4
5
6
7
8
9
10
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
  • 记住这个函数签名
1
func(w http.ResponseWriter, r *http.Request)
  • http.ResponseWriter
1
2
3
4
5
6
7
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(int)
}
  • http.Request
    • r.Method - HTTP method ( GET, POST, PUT, PATCH, DELETE etc.)
    • r.URL.Path - Request path (/things/123)
    • r.URL.String() - Full URL
    • r.URL.Query() - Query parameters (q=something&p=2)
    • r.Body - io.ReadCloser of the request body

Middleware

  • Middleware are just Go functions

Wrap

1
2
3
4
5
6
7
func Logging(h http.Handler, logger *log.Logger)http.Handler{
return http.HandlerFunc(w http.ResponseWriter, r *http.Request){
logger.Println("before")
defer logger.Println("after")
h.ServeHTTP(w, r)
}
}

Adapter pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Adapter wraps an http.Handler with additional functionality
type Adapter func(http.Handler) http.Handler

func Logging(logger *log.Logger) Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request{
logger.Println(r.Method, r.URL.Path)
h.ServeHTTP(w, r)
})
}
}

func WithHeader(key, value string) Adapter{
return func(h http.Handler) http.Handler{
return http.HanderFunc(func(w http.ResponseWriter, r *http.Request){
w.Header.Add(key, value)
h.ServeHTTP(w, r)
}
}
}

// Adapt handler with all specified adapters.
func Adapt(h http.Handler, adapters ...Adapter) http.Handler{
for _, adapter := range adapters{
h = adapter(h)
}
return h
}

func main(){
router := mux.NewRouter()

// adapt a single route
router.Handle("/", Adapt(someHandler, WithHeader("X-something", "Specific")))

// adapt all handlers
http.Handle("/", Adapt(router,
WithHeader("Server", "MyApp")
Logging(logger)
))
}

Alice

  • Alice 提供了一个更直观的方式,来使用中间件
  • 与Adapter类似,不过更直观
  • 实现如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
type Constructor func(http.Handler) http.Handler

type Chain struct{
constructors []Constructor
}

// New creates a new chain,
// memorizing the given list of middleware constructors.
// New serves no other function,
// constructors are only called upon a call to Then().
func New(constructors ...Constructor) Chain{
return Chain{append(([]Constructor)(nil), constructors...)}
}

func (c *Chain) Then(h http.Handler) http.Handler{
if h == nil{
h = http.DefaultServeMux
}
for i := range c.constructors{
h = c.constructors[len(c.constructors)-1-i](h)
}
return h
}

func (c *chain) ThenFunc(fn http.HandlerFunc) http.Handler{
if fn == nil{
return c.Then(nil)
}
return c.Then(fn)
}

func (c *Chain) Append(constructors ...Constructor) Chain{
newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
newCons = append(newCons, c.constructors...)
newCons = append(newCons, constructors...)

return Chain{newCons}
}

func (c *Chain) Extend(chain Chain) Chain {
return c.Append(chain.constructors...)
}

chain := alice.New(th.Throttle, timeoutHandler, nosurf.NewPure)
indexPipe := chain.Then(indexHandler)
authPipe := chain.Then(authHandler)

Server struct

  • 所有的组件都有一个单一的 server 结构
1
2
3
4
5
type server struct{
db *someDataBase
router *someRouter
email EmailSender
}
  • 共享的依赖是结构的字段 Shared dependencies are fields of the structure
  • 单一route文件,即一个route.go
  • handlers 挂在 server
1
func (s *server) handleSomething() http.HanderFunc{ ... }

易于测试

1
2
3
4
5
6
7
8
9
10
11
12
13
func TestHandleAbout(t *testing.T) {
is := is.New(t)
srv := server{
db: mockDatabase,
email: mockEmailSender,
}
srv.routes()
req, err := http.NewRequest("GET", "/about", nil)
is.NoErr(err)
w := httptest.NewRecorder()
srv.ServeHTTP(w, req)
is.Equal(w.StatusCode, http.StatusOK)
}

Return the handler

  • handler 函数实际上并不处理请求,而是返回一个处理请求的函数
  • 这为我们提供了一个闭包环境,handler可以在其中操作
1
2
3
4
5
6
func (s *server) handleSomething() http.HandlerFunc{
thing := prepareThing()
return func(w http.ResponseWriter, r *http.Request){
// use thing
}
}

prepareThing仅被调用一次,因此可以使用它对每个 handler 进行一次初始化,然后在handler 中使用 thing

通过参数获取特殊 handler 所需的依赖

1
2
3
4
5
func (s *server) handleGreeting(format string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, format, "World")
}
}

特殊handler所需的resquest和response struct可以定义在 handler 函数内

使用sync.Once设置依赖

  • 如果在准备 handler 时必须做任何昂贵的事情,则将其推迟到首次调用该 handler 时进行
  • 配合 sync.Once 保证代码只被执行一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func (s *server) handleTemplate(files string...) http.HandlerFunc{
var (
init sync.Once
tpl *template.Template
err error
)
return func(w http.ResponseWriter, r *http.Request){
init.Do(func(){
tpl, err = template.ParseFiles(files...)
})
if err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// use tpl
}
}
  • 错误检查在init函数之外,因此,如果确实有问题,仍然会发现错误,并且记录到日志