Skip to main content
Version: Latest

为了解决上一章节遗留的疑问:如何捕获返回的错误对象并作自定义的错误处理。 在本章节中,我们将会先简单介绍Web Server的中间件特性,再回答这个疑问。

中间件介绍

中间件是一种拦截器设计,在Web Server中可以拦截请求和返回结果,并在其前后进行自定义处理逻辑。

中间件的定义和普通的路由回调函数一样,但是可以在 Request 参数中使用 Middleware 属性对象来控制请求流程。

中间件的类型分为两种:前置中间件后置中间件。前置即在路由服务函数调用之前调用,后置即在其后调用。

func Middleware(r *ghttp.Request) {
// 前置中间件处理逻辑
r.Middleware.Next()
// 后置中间件处理逻辑
}

在中间件中执行完成处理逻辑后,使用 r.Middleware.Next() 方法进一步执行下一个流程; 如果这个时候直接退出不调用 r.Middleware.Next() 方法的话,将会退出后续的执行流程(例如可以用于请求的鉴权处理)。

使用中间件

我们使用中间件对上一章节的程序进行简单的改造,如下:

main.go
package main

import (
"context"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

type HelloReq struct {
g.Meta `path:"/" method:"get"`
Name string `v:"required" dc:"姓名"`
Age int `v:"required" dc:"年龄"`
}
type HelloRes struct{}

type Hello struct{}

func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
r := g.RequestFromCtx(ctx)
r.Response.Writef(
"Hello %s! Your Age is %d",
req.Name,
req.Age,
)
return
}

func ErrorHandler(r *ghttp.Request) {
// 执行路由回调函数
r.Middleware.Next()
// 判断是否产生错误
if err := r.GetError(); err != nil {
r.Response.Write("error occurs: ", err.Error())
return
}
}

func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ErrorHandler)
group.Bind(
new(Hello),
)
})
s.SetPort(8000)
s.Run()
}
  • 我们定义了一个错误处理的中间件ErrorHandler,在该中间件中我们先通过r.Middleware.Next()执行路由函数流程, 随后通过r.GetError()获取路由回调函数是否有错误产生。如果产生错误,那么直接输出错误信息。
  • 在路由注册中,我们通过group.Middleware(ErrorHandler)给该分组路由下的所有注册的路由,都绑定了错误处理的中间件。

执行结果

运行后,我们访问 http://127.0.0.1:8000/?name=john&age=18 可以看到,页面输出结果符合预期。

img.png

我们尝试一下错误的参数请求 http://127.0.0.1:8000/ 可以看到,页面输出结果也符合预期。

img_4.png

学习总结

我们使用中间件对请求错误进行自定义的处理,并捕获校验错误返回自定义的错误信息。 可以看到,中间件的功能非常灵活且强大,当然不仅仅局限于处理校验错误这种小场景。

试想一下,假如我们的项目中有很多接口,通常这些接口的输出格式都是固定的,例如都是json格式。 那么能否直接通过中间件的方式,统一返回的数据结构呢?答案当然是肯定的,我们将在下一章节介绍。