thinkphp 6 可用 middleware + db + request 实现无侵入 api 请求统计,需使用路由分组专用中间件、insert ... on duplicate key update 避并发冲突、after() 阶段过滤 options/错误码/爬虫请求,并建 date+route 联合唯一索引且确保写库生效。

直接说结论:ThinkPHP 6 的 middleware + Db + Request 就能稳稳实现 API 请求统计,不用改路由、不侵入业务逻辑,但必须避开「中间件执行时机」和「并发写入冲突」两个坑。
用什么中间件?选 app/middleware/ApiStatMiddleware.php 最合适
不能用全局中间件(比如 app/middleware.php 里注册的),否则会统计到静态资源、预检请求(OPTIONS)、甚至调试页面;也不能用控制器级中间件(太分散难维护)。推荐新建一个专用中间件,只对带 /api/ 前缀的路由生效:
return [
'api' => [
'middleware' => [
\app\middleware\ApiStatMiddleware::class,
],
'prefix' => 'api/',
],
];
这样既隔离了非 API 流量,又避免手动在每个控制器里加统计逻辑。
怎么记录才不丢数据?绕开 Db::insert() 直接写入
高频 API 下,Db::insert() 同步写库容易阻塞响应,且并发时可能因主键冲突或唯一索引报错(比如重复记录 date+route 组合)。实操建议:
- 用
Db::execute()执行INSERT ... ON DUPLICATE KEY UPDATE,主键设为date和route联合唯一 - 字段设计至少包含:
date(Y-m-d)、route($request->rule()->getRule())、count(默认 1,冲突时count = count + 1) - 不要记录
ip或user_id到这张表——它们属于明细表范畴,放这里会拖慢聚合写入
怎么区分「有效请求」?过滤掉预检、失败和爬虫
统计不是越多越好。真实场景中,要跳过三类请求:
-
OPTIONS请求:检查$request->method() !== 'OPTIONS' - 4xx/5xx 响应:在中间件
after()阶段读取$response->getCode(),只统计200和201 - 明显非人流量:检查
$request->header('user-agent')是否含curl、httpie、python-requests等关键词(简单匹配即可,不用正则全量识别)
注意:这些判断必须在 after() 里做,因为 before() 阶段还拿不到响应码。
统计结果查不出来?别忘了建联合索引
如果按 date 查日活、按 route 查接口热度,没索引时 SELECT COUNT(*) FROM api_stat WHERE date = '2024-06-01' 会全表扫描。必须立刻加:
ALTER TABLE `api_stat` ADD UNIQUE INDEX `uniq_date_route` (`date`, `route`);
另外,如果后续要支持「近7天趋势」,建议把 date 字段类型设为 DATE(不是 VARCHAR),否则范围查询走不了索引。
最常被忽略的是:统计中间件里用了 Db,但没在 config/database.php 中确认是否启用了 deploy 模式下的读写分离——写操作若被路由到从库,就永远写不进去了。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











