go-zero(八) 中间件的使用

go zero 中间件的使用

一、中间件介绍

中间件(Middleware)是一个在请求和响应处理之间插入的程序或者函数,它可以用来处理、修改或者监控 HTTP 请求和响应的各个方面。中间件通常位于 web 服务器和应用逻辑之间,它们可以拦截请求和响应流,对其进行某种处理,然后决定是否将控制传递给下一个中间件或最终的处理程序。

1.中间件的核心概念

  1. 请求拦截:中间件能够在请求到达目标处理器之前,对请求进行分析、修改或记录。

  2. 响应拦截:同样,中间件也可以在响应返回给客户端之前,对响应进行处理,例如添加 headers、压缩响应体、格式化响应数据等。

  3. 链式调用:多个中间件可以串联在一起, 形成一个中间件链,依次处理请求和响应。

2.中间件的用途

中间件可以用于以下多个方面:

  1. 日志记录:记录 HTTP 请求和响应的详细信息,例如请求路径、方法、状态码等。这有助于进行审计和调试。
  2. 认证和授权:验证请求是否具有必要的权限,确保用户或系统的身份是否合法。这对于保护敏感接口尤其重要。
  3. 请求限流:控制访问速率,避免服务被恶意请求或流量洪水淹没。这可以提高系统的稳定性和可用性。
  4. 跨域资源共享 (CORS):处理浏览器的跨域请求。中间件可以在响应中添加适当的 CORS 头,允许来自不同源的请求。
  5. 错误处理:捕获处理过程中的错误(如 panic)并生成适当的 HTTP 响应。这样可以确保系统在错误情况下不会崩溃,并能够正常返回相应。
  6. 数据格式化:统一请求和响应的数据格式,例如将响应数据转换为 JSON 格式,或者对请求中的参数进行验证和转换。
  7. 国际化 (i18n):处理用户的语言偏好和内容的本地化,使应用能够支持多语言。

3.中间件的工作流程

一般来说,工作流程如下:

  1. 用户发起请求,请求到达 Web 服务器。
  2. 中间件链开始处理:
    • 第一个中间件接收请求,进行相应的处理(如日志记录)。
    • 控制权传递给下一个中间件。
    • 这个过程可以一直持续,直到所有中间件处理完请求。
  3. 最终请求到达目标处理器,处理器生成响应。
  4. 响应同样会经过中间件链进行处理(如添加 headers)。
  5. 最终响应返回给用户。

这种结构类似于"洋葱模型":请求像是从外到内穿过洋葱层,而响应则是从内到外穿出。
在这里插入图片描述

二、go zero内置中间件介绍

go zero 提供了一系列内置的中间件,以帮助开发者管理 HTTP 请求的不同方面。

还记得之前的jwt鉴权吗?那边我们只实现了token生成,但是并没有实现验证,但是go zero能够自动解析token,就是因为它内置了AuthorizeHandler(鉴权管理中间件)

1. 内置中间件一览

在 go zero 中内置了如下中间件:

中间件名称功能描述默认状态
AuthorizeHandler身份验证与授权控制启用
BreakerHandler服务熔断,防止级联故障启用
LogHandler请求日志记录启用
MetricHandler服务指标统计启用
PrometheusHandlerPrometheus 监控指标收集启用
RecoverHandlerPanic 恢复,防止服务崩溃启用
TimeoutHandler请求超时控制启用
TraceHandler分布式链路追踪启用
MaxConnsHandler最大连接数限制启用
SheddingHandler负载均衡与请求分流启用
GunzipHandler响应压缩管理启用
MaxBytesHandler请求体大小限制启用
ContentSecurityHandler内容安全策略启用
CryptionHandler请求/响应加解密启用

这些中间件的具体实现可以去看github.com\zeromicro\go zero@v1.7.3\rest\handler目录的内容。

2. 核心中间件详解

认证中间件 (AuthorizeHandler)

JWT 认证是 Go-Zero 中最常用的身份验证方式,由内置的 AuthorizeHandler 提供支持。

工作原理

  1. 验证请求头中的 Authorization 字段
  2. 解析 JWT Token 并验证有效性
  3. 将用户信息存入上下文,供后续处理器使用

源码解析

// Authorize 函数用于创建一个 HTTP 中间件,该中间件可对传入的 HTTP 请求进行身份验证。
// 它会从请求中解析 JWT(JSON Web Token),验证其有效性,并将 JWT 中的自定义声明添加到请求的上下文(context)中,供后续处理程序使用。
// 参数 secret 是用于验证 JWT 的密钥,为字符串类型。
// 参数 opts 是可变参数,类型为 AuthorizeOption,用于配置身份验证的选项。
// 函数返回一个 func(http.Handler) http.Handler 类型的函数,即一个 HTTP 中间件。
func Authorize(secret string, opts ...AuthorizeOption) func(http.Handler) http.Handler {
	// 定义一个 AuthorizeOptions 类型的变量 authOpts,用于存储身份验证的选项。
	var authOpts AuthorizeOptions
	// 遍历传入的选项 opts
	for _, opt := range opts {
		// 调用每个选项函数,将 authOpts 的指针传递给它们,以设置具体的选项。
		opt(&authOpts)
	}

	// 创建一个 token.TokenParser 类型的实例 parser,用于解析 JWT。
	parser := token.NewTokenParser()

	// 返回一个中间件函数,该函数接受一个 http.Handler 类型的参数 next,表示下一个处理程序。
	return func(next http.Handler) http.Handler {
		// 返回一个 http.HandlerFunc 类型的处理程序,用于处理传入的 HTTP 请求。
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// 调用 parser 的 ParseToken 方法,从请求 r 中解析 JWT,同时传入当前密钥 secret 和之前的密钥 authOpts.PrevSecret。
			// tok 是解析出的 JWT 对象,err 是可能出现的错误。
			tok, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
			if err != nil {
				// 如果解析过程中出现错误,说明 token 无效或解析失败。
				// 调用 unauthorized 函数返回未授权错误,并终止请求处理。
				unauthorized(w, r, err, authOpts.Callback)
				return
			}

			// 检查解析出的 JWT 是否有效。
			if !tok.Valid {
				// 如果 JWT 无效,调用 unauthorized 函数返回未授权错误,并终止请求处理。
				unauthorized(w, r, errInvalidToken, authOpts.Callback)
				return
			}

			// 尝试将 JWT 的声明(Claims)转换为 jwt.MapClaims 类型。
			// claims 是转换后的声明对象,ok 表示转换是否成功。
			claims, ok := tok.Claims.(jwt.MapClaims)
			if !ok {
				// 如果转换失败,说明 JWT 中没有有效的声明。
				// 调用 unauthorized 函数返回未授权错误,并终止请求处理。
				// errNoClaims 是一个预定义的错误对象,表示没有声明。
				unauthorized(w, r, errNoClaims, authOpts.Callback)
				return
			}

			// 获取请求的上下文 ctx。
			ctx := r.Context()
			// 遍历 JWT 的声明,将自定义声明添加到上下文中。
			for k, v := range claims {
				switch k {
				// 忽略标准的 JWT 声明,如受众、过期时间、ID 等。
				case jwtAudience, jwtExpire, jwtId, jwtIssueAt, jwtIssuer, jwtNotBefore, jwtSubject:
					// 忽略标准的声明,不做处理
				default:
					// 将自定义声明添加到上下文中,键为 k,值为 v。
					ctx = context.WithValue(ctx, k, v)
				}
			}

			// 将带有更新后的上下文的请求传递给下一个处理程序 next 进行处理。
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}
熔断中间件 (BreakerHandler)

熔断器是微服务架构中保障系统稳定性的关键组件,它能在服务出现故障时快速熔断,防止故障扩散。

工作原理

  1. 监控 API 调用的成功/失败状态
  2. 当失败率达到阈值时触发熔断
  3. 熔断期间快速拒绝请求,避免对故障服务持续请求
  4. 熔断一段时间后进入半开状态,尝试恢复服务

源码解析

// BreakerHandler 返回一个熔断中间件。熔断机制用于在系统出现故障或过载时,暂时切断对特定服务的访问,避免系统进一步恶化。
// 参数 method 表示 HTTP 请求的方法(如 GET、POST 等),path 表示请求的路径,metrics 用于统计请求的指标信息。
func BreakerHandler(method, path string, metrics *stat.Metrics) func(http.Handler) http.Handler {
	// 创建一个新的熔断实例 brk。使用 method 和 path 组合作为熔断实例的名称,便于区分不同的路由。
	// breakerSeparator 是一个分隔符,用于连接 method 和 path 形成唯一的名称。
	brk := breaker.NewBreaker(breaker.WithName(strings.Join([]string{method, path}, breakerSeparator)))
	// 返回一个函数,该函数接受一个 http.Handler 类型的参数 next,表示下一个处理程序。
	return func(next http.Handler) http.Handler {
		// 返回一个 http.HandlerFunc 类型的处理程序,用于处理传入的 HTTP 请求。
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// 尝试从熔断实例 brk 中获取执行许可,返回一个 promise 和可能的错误。
			// 如果熔断打开(即不允许执行),则会返回错误。
			promise, err := brk.Allow()
			if err != nil {
				// 如果获取许可失败,说明熔断已经打开,需要进行相应处理。
				// 增加统计指标中的丢弃请求数量。
				metrics.AddDrop()
				// 记录错误日志,包含请求的 URI、远程地址和用户代理信息。
				logc.Errorf(r.Context(), "[http] dropped, %s - %s - %s",
					r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent())
				// 返回 HTTP 503 状态码,表示服务不可用。
				w.WriteHeader(http.StatusServiceUnavailable)
				return
			}

			// 创建一个自定义的响应写入器 cw,用于记录响应的状态码。
			cw := response.NewWithCodeResponseWriter(w)
			// 使用 defer 关键字确保在函数返回时执行以下代码块。
			defer func() {
				// 根据响应的状态码判断请求是否成功。
				// 如果状态码小于 500(即非内部服务器错误),表示请求成功。
				if cw.Code < http.StatusInternalServerError {
					// 调用 promise 的 Accept 方法,表示请求成功,熔断实例可以继续正常工作。
					promise.Accept()
				} else {
					// 如果状态码大于等于 500,表示请求失败。
					// 调用 promise 的 Reject 方法,并传入错误信息,可能会触发熔断打开。
					promise.Reject(fmt.Sprintf("%d %s", cw.Code, http.StatusText(cw.Code)))
				}
			}()
			// 将请求传递给下一个处理程序进行处理。
			next.ServeHTTP(cw, r)
		})
	}
}
链路追踪中间件 (TraceHandler)

在微服务架构中,一个用户请求可能涉及多个服务调用。链路追踪帮助开发者了解请求在服务间的流转路径。

工作原理

  1. 为每个请求生成唯一的 TraceID
  2. 在服务间传递 TraceID
  3. 记录请求在各个服务中的耗时
  4. 构建完整的调用链路图

源码解析


// TraceHandler 返回一个用于处理 OpenTelemetry 追踪的中间件。
// 该中间件会为每个符合条件的 HTTP 请求创建一个新的追踪跨度(span),并记录请求的相关信息。
// 参数 serviceName 表示服务的名称,用于在追踪信息中标识服务。
// 参数 path 表示请求路径,用于指定追踪跨度的名称。
// 参数 opts 是可变参数,用于配置追踪选项。
func TraceHandler(serviceName, path string, opts ...TraceOption) func(http.Handler) http.Handler {
	// 定义一个 traceOptions 类型的变量 options,用于存储追踪选项
	var options traceOptions
	// 遍历传入的选项 opts
	for _, opt := range opts {
		// 调用每个选项函数,将 options 的指针传递给它们,以设置具体的选项
		opt(&options)
	}

	// 创建一个集合 ignorePaths,用于存储需要忽略的请求路径
	ignorePaths := collection.NewSet()
	// 将 options.traceIgnorePaths 中的所有路径添加到集合中
	ignorePaths.AddStr(options.traceIgnorePaths...)

	// 返回一个中间件函数,该函数接受一个 http.Handler 类型的参数 next,表示下一个处理程序
	return func(next http.Handler) http.Handler {
		// 创建一个 OpenTelemetry 追踪器 tracer
		tracer := otel.Tracer(trace.TraceName)
		// 获取 OpenTelemetry 的文本映射传播器 propagator
		propagator := otel.GetTextMapPropagator()

		// 中间件函数返回一个 http.HandlerFunc 类型的处理程序,用于处理传入的 HTTP 请求
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// 初始化跨度名称为传入的 path
			spanName := path
			// 如果 path 为空,则使用请求的 URL 路径作为跨度名称
			if len(spanName) == 0 {
				spanName = r.URL.Path
			}

			// 检查 spanName 是否在忽略路径集合中
			if ignorePaths.Contains(spanName) {
				// 如果在忽略路径集合中,则直接调用下一个处理程序,不进行追踪
				next.ServeHTTP(w, r)
				return
			}

			// 使用传播器从请求头中提取上下文信息
			ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
			// 使用追踪器启动一个新的跨度
			// spanCtx 是新创建的跨度上下文,span 是新创建的跨度对象
			// oteltrace.WithSpanKind(oteltrace.SpanKindServer) 设置跨度类型为服务器端跨度
			// oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(serviceName, spanName, r)...) 添加 HTTP 请求的相关属性
			spanCtx, span := tracer.Start(
				ctx,
				spanName,
				oteltrace.WithSpanKind(oteltrace.SpanKindServer),
				oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(
					serviceName, spanName, r)...),
			)
			// 使用 defer 关键字确保在函数返回时结束跨度
			defer span.End()

			// 使用传播器将跨度上下文信息注入到响应头中,方便后续追踪
			propagator.Inject(spanCtx, propagation.HeaderCarrier(w.Header()))

			// 创建一个自定义的响应写入器 trw,用于记录响应的状态码
			trw := response.NewWithCodeResponseWriter(w)
			// 将带有更新后的上下文的请求传递给下一个处理程序进行处理
			next.ServeHTTP(trw, r.WithContext(spanCtx))

			// 根据响应的状态码设置跨度的属性
			span.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(trw.Code)...)
			// 根据响应的状态码和跨度类型设置跨度的状态
			span.SetStatus(semconv.SpanStatusFromHTTPStatusCodeAndSpanKind(
				trw.Code, oteltrace.SpanKindServer))
		})
	}
}

3. 中间件配置与管理

以上中间件都是默认启用,go-zero 允许我们通过配置文件灵活控制中间件的启用/禁用状态:

如果你看过RestConf 的代码,就应该能看到,它里面包含了一个Middlewares 中间件的配置结构。

	RestConf struct {
		service.ServiceConf
		Host     string `json:",default=0.0.0.0"`
		Port     int
		/*
		....
		*/
		//中间件配置相关的结构体
		Middlewares MiddlewaresConf
		// TraceIgnorePaths is paths blacklist for trace middleware.
	
	}

我们可以看下它的具体内容,可以看到这个结构体主要用来控制,这些中间件是否启动,默认都是开启状态:

MiddlewaresConf struct {
		Trace      bool `json:",default=true"`
		Log        bool `json:",default=true"`
		Prometheus bool `json:",default=true"`
		MaxConns   bool `json:",default=true"`
		Breaker    bool `json:",default=true"`
		Shedding   bool `json:",default=true"`
		Timeout    bool `json:",default=true"`
		Recover    bool `json:",default=true"`
		Metrics    bool `json:",default=true"`
		MaxBytes   bool `json:",default=true"`
		Gunzip     bool `json:",default=true"`
	}

那么如何控制这些中间件是否关闭就很简单了,例如我想关闭Prometheus中间件,打开yaml文件:

Middlewares:
  Prometheus: false #把值设置为false

三、自定义中间件

尽管go-zero 提供了丰富的内置中间件,但在实际项目中,我们经常需要开发自定义中间件来满足特定业务需求。

1.局部中间件

局部中间件仅应用于特定路由组或服务块,是实现业务逻辑分离的有效手段。

步骤 1: 在 API 文件中声明中间件

在 go zero 中,我们通过 api 语言来声明 HTTP 服务,然后通过 goctl 生成 HTTP 服务代码。

type (
	RegisterRequest {
		//请求体定义了 Username 和Password 字段, 并且都设置了不能为空
		Username string `json:"username" validate:"required"`
		Password string `json:"password" validate:"required"`
	}
	RegisterResponse {
		//响应体 定义类一个Message  用来返回结果
		Message string `json:"message"`
	}

	LoginRequest {
		Username string `json:"username" validate:"required"`
		Password string `json:"password" validate:"required"`
	}
	LoginResponse {
		Token string `json:"token"`
	}
)

@server (
	group:      user // 代表当前 service 代码块下的路由生成代码时都会被放到 user 目录下
	prefix:     /v1 //定义路由前缀为 "/v1"
	middleware: TestMiddleware    
)

service user {
    @handler Register
    post /register (RegisterRequest) returns (RegisterResponse)
    
    @handler Login
    post /login (LoginRequest) returns (LoginResponse)
}

在上面的例子中,我们声明了一个中间件TestMiddleware,然后在 @server 中通过 middileware 关键字来声明中间件。

  middleware: UserRateLimiter, RequestValidator  // 声明多个中间件

也可以这样声明多个中间件

需要说明的,我们这个中间件是局部中间件,仅对当前的server有效 #EE3F4D

使用以下命令更新代码:

goctl api go --api user.api --dir .

命令执行完后,会在项目中生成middleware文件夹,以及中间件相关代码

我们可以先看下routes.go文件,可以看到这路由前面帮我加了一个中间件
在这里插入图片描述

步骤 2: 实现中间件逻辑

接下来我们打开middleware目录下的文件,我们简单的添加一个header信息,修改代码:

func (m *TestMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// TODO generate middleware implement function, delete after code implementation
		//我们自定义一个header信息
		w.Header().Set("xxxx", "aaaabb")
		next(w, r)
	}
}
步骤 3: 在 ServiceContext 中注册中间件

然后把中间件注册到服务中,打开servicecontext.go文件:

type ServiceContext struct {
	Config                config.Config
	UserModel             model.UsersModel
	UserRpc               user.User
	TestMiddleware rest.Middleware  //定义中间件
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:                c,
		UserModel:             model.NewUsersModel(sqlx.NewMysql(c.MysqlDB.DbSource)),
		UserRpc:               user.NewUser(zrpc.MustNewClient(c.UserRpcConf)),
		//初始化中间件
		TestMiddleware Middleware: middleware.NewTestMiddleware Middleware().Handle,
	}
}

运行项目测试
在这里插入图片描述

2.进阶实现限流中间

现在来实现一个局部的限流中间件 ,更好的理解中间件。

步骤 1: 在 API 文件中声明中间件

@server (
	group:      user // 代表当前 service 代码块下的路由生成代码时都会被放到 user 目录下
	prefix:     /v1 //定义路由前缀为 "/v1"
	middleware: UserRateLimiter //更换成限流中间件
)
service user {
	@handler Register
	post /register (RegisterRequest) returns (RegisterResponse)

	@handler Login
	post /login (LoginRequest) returns (LoginResponse)
}


使用以下命令更新代码:

goctl api go --api user.api --dir .
步骤 2: 实现中间件逻辑

修改internal/middleware/userratelimitermiddleware.go文件:


type UserRateLimiterMiddleware struct {
	// 以用户 IP 为 key,记录请求时间
	requestRecords map[string][]time.Time
	mutex          sync.Mutex
	// 时间窗口内允许的最大请求次数
	maxRequests int
	// 时间窗口大小 (秒)
	windowSize time.Duration
}

func NewUserRateLimiterMiddleware() *UserRateLimiterMiddleware {
	return &UserRateLimiterMiddleware{
		requestRecords: make(map[string][]time.Time),
		maxRequests:    1,   //为了方便演示,把最大请求改为1
		windowSize:     time.Second * 10, //窗口时间设置为10秒
	}
}

func (m *UserRateLimiterMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// TODO generate middleware implement function, delete after code implementation
		// 获取客户端 IP
		clientIP := r.RemoteAddr
		fmt.Println(clientIP)
		m.mutex.Lock()
		defer m.mutex.Unlock()

		now := time.Now()

		// 清理过期的请求记录
		if records, exists := m.requestRecords[clientIP]; exists {
			var validRecords []time.Time
			for _, t := range records {
				if now.Sub(t) <= m.windowSize {
					validRecords = append(validRecords, t)
				}
			}
			m.requestRecords[clientIP] = validRecords
		}

		// 检查请求频率
		if len(m.requestRecords[clientIP]) >= m.maxRequests {
			logx.Infof("IP %s 请求频率过高", clientIP)

			http.Error(w, "请求过于频繁,请稍后再试", http.StatusTooManyRequests)

		}

		// 记录本次请求
		m.requestRecords[clientIP] = append(m.requestRecords[clientIP], now)

		// 继续处理请求
		next(w, r)
	}
}
步骤 3: 实现登录逻辑

修改loginlogic.go ,实现简单的登录逻辑,用来测试

func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
	// todo: add your logic here and delete this line
	if req.Username == "admin" && req.Password == "123456" {
		return &types.LoginResponse{
			Token: "admin-token",
		}, nil
	}
	return
}


步骤 4: 测试中间件

运行项目,不断的点击请求, 发现中间件启用

在这里插入图片描述

3. 全局中间件

全局中间件适用于所有路由,常用于实现通用功能,如 CORS 支持、Gzip 压缩等。

步骤1:实现请求计时中间件

我们通过实现一个请求计时中间件来演示

package middleware

import (
    "net/http"
    "time"
    
    "github.com/zeromicro/go-zero/core/logx"
)
// middleware/timer.go
func RequestTimer(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        // 调用下一个处理函数
        next(w, r)
        
        // 计算请求处理时间
        elapsed := time.Since(start)
        logx.Infof("请求: %s %s 处理时间: %v", r.Method, r.URL.Path, elapsed)
    }
}
步骤2:注册全局中间件

然后将其注册到 go zero 的 rest 中

func main() {
	flag.Parse()

	var c config.Config
	conf.MustLoad(*configFile, &c)

	server := rest.MustNewServer(c.RestConf)
	defer server.Stop()

	server.Use(middleware.RequestTimer) // 注册请求计时器中间件

	ctx := svc.NewServiceContext(c)
	handler.RegisterHandlers(server, ctx)

	fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
	server.Start()
}

步骤3:测试中间件

在这里插入图片描述

四、中间件最佳实践

遵循以下原则可以帮助你设计出高质量的中间件:

  • 单一职责:每个中间件只负责一项特定功能
  • 高内聚低耦合:减少中间件间的相互依赖
  • 高性能:避免在中间件中执行耗时操作
  • 可配置性:通过配置控制中间件行为
  • 幂等性:多次应用同一中间件不会产生副作用
  • 透明性:中间件不应改变请求和响应的语义

2. 中间件顺序与性能

中间件的执行顺序对于系统行为和性能有显著影响:

客户端 --> [日志] --> [限流] --> [认证] --> 业务逻辑 --> [压缩] --> [CORS] --> 客户端

建议的中间件执行顺序:

  1. 记录与监控:日志、链路追踪等
  2. 安全防护:限流、熔断等
  3. 身份验证:认证、授权等
  4. 请求处理:请求解析、参数验证等
  5. 响应处理:响应压缩、CORS 等
Go-Zero框架内置了许多方便使用中间件Middleware),它们可以在处理HTTP请求和响应之前或之后执行一些自定义操作。中间件的基本用法是在创建`Server`实例时链式调用`Use`方法,然后在`Group`或直接在顶级路径上注册处理函数。 以下是使用内置中间件的简单示例: ```go import ( "github.com/go-zoo/zoo" "github.com/go-zoo/zoo/middleware" ) func main() { // 创建一个Server实例 server := zoo.NewServer() // 添加日志记录中间件,记录所有请求和响应 server.Use(middleware.Logger()) // 访问控制中间件,检查用户是否已登录 authMiddleware := middleware.Auth(func(ctx *zoo.Context, next zoo.HandlerFunc) error { // 这里可以检查token或者其他认证信息 // 如果验证通过,返回nil继续处理;否则返回错误 }) server.Use(authMiddleware) // 错误处理中间件,捕获并统一处理各种异常情况 server.Use(middleware.Recover()) // 使用`Group`定义特定的API路径和对应的处理器 api := server.Group("/api") api.Use(middleware.Cors()) // 允许跨域访问 // API处理器 api.GET("/hello", helloHandler) // 启动服务器 if err := server.Run(); err != nil { panic(err) } // 示例处理器函数 func helloHandler(c *zoo.Context) error { c.JSON(200, gin.H{"message": "Hello from Go-Zero!"}) return nil } } ``` 在这个例子中,每添加一个中间件,它都会对后续的处理器执行操作,直到遇到下一个中间件或最终的处理器函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值