golang基于net/context的链式处理器包装中间件插件库chain的使用

Golang基于net/context的链式处理器包装中间件插件库chain的使用

chain是一个帮助构建嵌套http.Handler实例的Golang包。

安装

go get github.com/codemodus/chain

使用

基本用法

type Chain
    func New(handlers ...func(http.Handler) http.Handler) *Chain
    func (c *Chain) Append(handlers ...func(http.Handler) http.Handler) *Chain
    func (c *Chain) Copy(chain *Chain)
    func (c *Chain) End(handler http.Handler) http.Handler
    func (c *Chain) EndFn(handlerFunc http.HandlerFunc) http.Handler
    func (c *Chain) Merge(chains ...*Chain) *Chain

示例代码

import (
    // ...

    "github.com/codemodus/chain"
)

func main() {
    // ...

    // 嵌套处理器会在调用ServeHTTP()前后向响应体写入"0"或"1"
    // endHandler会向响应体写入"_END_"

    ch00 := chain.New(nestedHandler0, nestedHandler0)
    ch001 := ch00.Append(nestedHandler1)

    ch1 := chain.New(nestedHandler1)
    ch1001 := ch1.Merge(ch001)

    mux := http.NewServeMux()
    mux.Handle("/00_End", ch00.EndFn(endHandler))     // 响应体: "00_END_00"
    mux.Handle("/001_End", ch001.EndFn(endHandler))   // 响应体: "001_END_100"
    mux.Handle("/1001_End", ch1001.EndFn(endHandler)) // 响应体: "1001_END_1001"

    // ...
}

可嵌套的http.Handler

func nestableHandler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // ...
        
        next.ServeHTTP(w, r)
    	
        // ...
    })
}

Go 1.7+/chain 2.0+的变化

从Go 1.7开始,http包的Request类型包含一个字段(通过Context()方法访问),该字段持有context.Context的实现。此外,context包已被添加到标准库中。现在不再需要之前版本的chain中定义的自定义Handler。

性能基准

这些结果是比较正常嵌套函数和链式函数的性能。每个基准测试在最终处理程序之前包含10个函数。

go1.7
benchmark             iter      time/iter   bytes alloc         allocs
---------             ----      ---------   -----------         ------
BenchmarkChain10     20000    61.01 μs/op     3684 B/op   51 allocs/op
BenchmarkChain10-4   20000    68.62 μs/op     3691 B/op   51 allocs/op
BenchmarkChain10-8   20000    69.33 μs/op     3696 B/op   51 allocs/op
BenchmarkNest10      20000    60.36 μs/op     3684 B/op   51 allocs/op
BenchmarkNest10-4    20000    70.82 μs/op     3692 B/op   51 allocs/op
BenchmarkNest10-8    20000    71.03 μs/op     3697 B/op   51 allocs/op

更多关于golang基于net/context的链式处理器包装中间件插件库chain的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang基于net/context的链式处理器包装中间件插件库chain的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 基于 net/context 的链式处理器中间件库 chain 使用指南

在 Go 语言中,构建中间件链是 Web 开发中常见的模式。下面我将介绍如何使用 net/context 构建链式处理器中间件,并提供一个简单但功能完整的实现。

基本概念

链式中间件允许你将多个处理器函数串联起来,按顺序执行,每个中间件可以在请求处理前后执行特定逻辑。

核心实现

package chain

import (
	"context"
	"net/http"
)

// Handler 是一个函数类型,接收 context 并返回错误
type Handler func(context.Context) error

// Middleware 是中间件类型,接收一个 Handler 并返回一个新的 Handler
type Middleware func(Handler) Handler

// Chain 结构体包含中间件列表
type Chain struct {
	middlewares []Middleware
}

// New 创建一个新的中间件链
func New(middlewares ...Middleware) Chain {
	return Chain{middlewares: append(([]Middleware)(nil), middlewares...)}
}

// Then 将中间件应用到最终的处理器
func (c Chain) Then(h Handler) Handler {
	for i := range c.middlewares {
		h = c.middlewares[len(c.middlewares)-1-i](h)
	}
	return h
}

// Append 向链中添加新的中间件
func (c Chain) Append(middlewares ...Middleware) Chain {
	newMiddlewares := make([]Middleware, 0, len(c.middlewares)+len(middlewares))
	newMiddlewares = append(newMiddlewares, c.middlewares...)
	newMiddlewares = append(newMiddlewares, middlewares...)
	
	return Chain{middlewares: newMiddlewares}
}

// HTTPHandler 将 Handler 转换为 http.Handler
func HTTPHandler(h Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if err := h(ctx); err != nil {
			// 处理错误
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	})
}

使用示例

下面展示如何使用这个链式中间件库:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"time"
)

func main() {
	// 创建中间件链
	chain := New(
		loggingMiddleware,
		timeoutMiddleware(2 * time.Second),
		authMiddleware,
	)

	// 创建最终处理器
	handler := chain.Then(func(ctx context.Context) error {
		// 业务逻辑
		fmt.Println("处理请求...")
		time.Sleep(1 * time.Second) // 模拟处理时间
		return nil
	})

	// 转换为 http.Handler 并启动服务
	http.Handle("/", HTTPHandler(handler))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

// 日志中间件
func loggingMiddleware(next Handler) Handler {
	return func(ctx context.Context) error {
		start := time.Now()
		fmt.Printf("开始处理请求 at %v\n", start)
		
		err := next(ctx)
		
		fmt.Printf("请求处理完成, 耗时: %v\n", time.Since(start))
		return err
	}
}

// 超时中间件
func timeoutMiddleware(timeout time.Duration) Middleware {
	return func(next Handler) Handler {
		return func(ctx context.Context) error {
			ctx, cancel := context.WithTimeout(ctx, timeout)
			defer cancel()
			return next(ctx)
		}
	}
}

// 认证中间件
func authMiddleware(next Handler) Handler {
	return func(ctx context.Context) error {
		// 这里可以检查认证信息
		fmt.Println("检查认证...")
		return next(ctx)
	}
}

高级用法

1. 条件中间件

func conditionalMiddleware(condition bool, mw Middleware) Middleware {
	if condition {
		return mw
	}
	return func(next Handler) Handler {
		return next
	}
}

// 使用示例
chain := New(
	conditionalMiddleware(config.EnableLogging, loggingMiddleware),
	// 其他中间件...
)

2. 错误处理中间件

func errorHandlingMiddleware(next Handler) Handler {
	return func(ctx context.Context) error {
		err := next(ctx)
		if err != nil {
			// 记录错误或执行其他处理
			fmt.Printf("发生错误: %v\n", err)
		}
		return err
	}
}

3. 上下文值传递

func requestIDMiddleware(next Handler) Handler {
	return func(ctx context.Context) error {
		reqID := "req-" + uuid.New().String()
		ctx = context.WithValue(ctx, "requestID", reqID)
		return next(ctx)
	}
}

// 在处理器中获取
func businessHandler(ctx context.Context) error {
	reqID := ctx.Value("requestID").(string)
	fmt.Println("Request ID:", reqID)
	// 业务逻辑...
	return nil
}

总结

这个链式中间件库提供了以下优势:

  1. 灵活性:可以自由组合中间件
  2. 可读性:清晰的链式调用结构
  3. 可扩展性:易于添加新的中间件
  4. context 集成:充分利用 Go 的 context 包

你可以根据项目需求扩展这个基础实现,添加更多功能如性能监控、请求验证等中间件。

回到顶部