洋葱模型
洋葱模型中,每一层相当于一个中间件,用于处理特定功能
假设洋葱中间是我们的handler处理程序,则处理顺序应该是:
handler前请求→handler→handler后响应
也就是每个中间件都有两次处理时机
因此我们一般将一些需要前置处理的,比如拦截登录token之类的请求,放在handler前面,而需要后置处理的,比如收集方法耗时之类的信息,放在handler后面,如以下代码:
1 | authCtrl := &ctrl.AuthController{} |
输出为
1 | 2026/07/16 20:42:04 login1 before |
功能
gRPC中的中间件是拦截器
UnaryInterceptor
一元拦截器,用于一次请求-一次响应的RPC调用
ChainUnaryInterceptor
这样可以使用多个一元拦截器,如
1 | s := grpc.NewServer( |
执行顺序按照洋葱模型
例-auth中间件
以下是一个验证登录的中间件,服务端代码
1 | s := grpc.NewServer( |
- 通过metadata的方法取出需要的中间件
- 验证token,若长度大于0则取出判断是不是需要的,若是则拦截
- 否则放过
客户端代码:
1 | md := metadata.Pairs("key", "value","token", "123") |
- 使用pair可以创建多个metadata,奇数为key,偶数为value
- 出站使用outgoing获取带有token的context,并传入login
http请求存在一个header和一个body,body内存储我们的请求消息,而header存储着JWT token,request ID等数据,这些数据就是用metadata来承载的
metadata中存在两种key:
- incoming:服务内部方法之间传递
- outgoing:出入当前服务与其他服务传递
gRPC中提供了读取和发送metadata的方法:
FromOutgoing/IncomingContextNewIncoming/OutgoingContextAppendToOutgoingContextnew会直接覆盖metadata,append则会追加
若服务端实现了authFuncOverride方法,我们的中间件就会直接调用服务端实现的auth方法
在ctrl/middleware.go中实现该方法,就可以绕过authFunc
实现此方法的原因是我们需要对不同的方法进行不同操作,如Login方法不需要鉴权,而其他方法需要
1 | func (AuthController) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) { |