// Handler defines the handler invoked by Interceptor to complete the normal execution.
type Handler func(ctx context.Context, req any) (resp any, err error)
// Interceptor provides a hook to intercept the execution.
// It is the responsibility of the interceptor to invoke handler to complete the execution.
type Interceptor func(ctx context.Context, req any, handler Handler) (resp any, err error)
// chainInterceptors chains interceptors into one. The first interceptor will be the outer most,
// while the last interceptor will be the inner most wrapper around handler.
func chainInterceptors(interceptors []Interceptor) Interceptor {
return func(ctx context.Context, req any, handler Handler) (resp any, err error) {
return interceptors[0](ctx, req, getChainedHandler(interceptors, 0, handler))
}
}
func getChainedHandler(interceptors []Interceptor, curr int, finalHandler Handler) Handler {
if curr == len(interceptors)-1 {
return finalHandler
}
return func(ctx context.Context, req any) (resp any, err error) {
return interceptors[curr+1](ctx, req, getChainedHandler(interceptors, curr+1, finalHandler))
}
}
若干个实现了相同接口的 handler 串成一条职责链,请求在链条中向下传递,每个 handler 都可以决定是否处理请求以及是否将请求传递给下一个 handler。
有两种模式:
一种函数式编程实现
关于面向对象的写法,我之前写过一个。在这种写法中,每个 handler 都要保存下一个 handler 的引用,不然它无法向下传递。
这里分析的主要是函数式编程的实现方法。这是一段来自 github.com/grpc/grpc-go 的代码,做了一些简化:
若干
Interceptor
和Handler
串成了一条责任链。请求经过Interceptor
的拦截处理,最后由Handler
完成真正的响应。每个Interceptor
都可以决定是否调用下一个Interceptor
,最后一个Interceptor
可以决定是否调用Handler
。chainInterceptors
将多个Interceptor
组合成一个Interceptor
。前一个Interceptor
包裹后一个Interceptor
,所有的Interceptor
层层包裹,第一个Interceptor
在最外面,最后一个在最里面(是洋葱模型?)。请求到达时,先由外向内进入,再由内向外返回。See also
https://refactoring.guru/design-patterns/chain-of-responsibility