Go 中间件写多厚合适,我见过两个极端
我见过一个项目的中间件目录里躺着十来个文件,光认证就拆了四个:解析令牌、校验权限、刷新会话、写审计日志。也见过另一个项目,所有校验逻辑塞在一个函数里,两百行,谁都不敢动。Go 中间件该写多厚,我的答案是:只做「跟业务无关、且对所有请求都一样」的事,超过这个范围就该往外挪。
一、Go 中间件该写多厚:先看第一个坑,把业务规则塞进中间件
现象是中间件里出现了「如果这个用户是商家就放行、是普通用户就查一下订单」。
为什么踩:中间件拿得到请求,顺手就能写判断,写起来确实方便。但这个判断依赖具体的业务实体,意味着中间件要知道业务模型。
后果是它没法复用,也没法单独测试。你想验证权限逻辑,得先构造出一个完整的请求上下文。
怎么避:中间件只处理两类东西——请求本身携带的信息(令牌、头、来源),和对所有接口一视同仁的规则(限流、日志、超时)。出现业务分支就该挪到处理函数里。
二、中间件层层套,顺序谁也说不清
现象是中间件注册了七八个,出了问题时没人能说清它们的执行顺序。
为什么踩:多数框架里中间件的顺序由注册顺序决定,但注册代码可能分散在好几个文件里。加的人只管加,没人回头理顺序。
最典型的翻车是日志中间件记不到真实状态码——因为它在写响应的中间件之前执行,读到的是默认值。
怎么避:把中间件注册集中在一处,按执行顺序从上到下排好,每行加一句注释说明它在干什么。顺序有依赖的,紧挨着放。
三、在中间件里做慢操作
现象是接口整体变慢,但看业务代码又没什么耗时操作。
为什么踩:中间件对每个请求都执行,把一次几十毫秒的远程调用放进去,等于给所有接口都加了一份固定开销。而且这个开销在业务代码里完全看不见。
怎么避:中间件里不做远程调用,不做磁盘读写,不做复杂计算。必须做的(比如查权限),加缓存。实在慢,就把它从中间件里拿出来,只在需要它的处理函数里调用。
四、错误处理各写一套
现象是有的中间件直接写响应,有的返回错误给上层,有的只打日志。最后客户端收到的错误格式五花八门。
为什么踩:每个中间件是不同时间加的,各自解决了当时的问题,没统一约定。
怎么避:先定一个约定:中间件要么只负责往上下文里放数据,要么统一走一套错误返回。别两种混着来。写响应这件事只留一个出口。
止损清单
发现中间件层出问题了,我按这个顺序收拾。
第一,数一遍有几个。超过五个就该审视,是不是有本该合并的。
第二,画出执行顺序。按注册顺序列一遍,标出哪些之间有依赖。
第三,逐个问一句:它对所有请求都一样吗?不一样的那个,就是该挪走的。
第四,把慢操作全部标出来,逐个决定是加缓存还是拿出去。第五,统一错误出口。
五、我现在的做法
判断某个逻辑该不该是中间件,我只问一个问题:这个逻辑如果被删掉,会不会影响某几个特定接口,还是会影响全部接口?只影响几个特定接口的,它不是中间件。
另外,跨请求共享的数据怎么传,这个约定要早定。是用上下文里的值,还是显式传参,选一个并写进项目说明。混用是后面所有混乱的源头,也是 Go 中间件该写多厚这个问题最早的答案。
常见问题
Q:Go 中间件该写多厚?
A:只放跟业务无关、对所有请求一视同仁的逻辑,比如日志、限流、超时、认证。出现业务分支就该挪到处理函数里。
Q:中间件数量控制在几个以内比较好?
A:没有硬性数字,但如果超过五个就该检查一遍。数量本身不是问题,说不清顺序和职责才是。
Q:中间件里能不能查数据库?
A:能,但必须加缓存。它对每个请求都执行,一次远程调用会变成所有接口的固定开销。
相关阅读
- Go 里 channel 和 mutex 该用哪个
- Go 单体服务怎么分层,一个人维护够用
- Go 接口设计留扩展点,我在三处收手
- Go 服务优雅退出:三步不断掉请求
- Go 配置放文件还是环境变量,我踩坑后定了
本文作者西米,37 岁程序员,一个人加五个 AI 员工在跑一家公司。踩过的坑和真实账本都写在公众号「西米分享」。
完整代码、踩坑清单与最新进展,都在公众号。
📱 关注公众号「西米分享」