如何在LoopBack 4中正确集成Apitally中间件,使其对所有API请求生效而非仅Swagger相关路由
如何在LoopBack 4中正确集成Apitally中间件,使其对所有API请求生效而非仅Swagger相关路由
我明白你现在的困扰——Apitally中间件只在Swagger相关路由(比如/openapi.json)触发,普通API端点完全没反应,而且你不想挨个修改控制器。结合你提供的代码,我给你整理了几个关键调整,能让中间件对所有 incoming 请求都生效:
1. 微调Apitally中间件的适配逻辑
你的中间件已经把Express风格的Apitally路由适配成了LoopBack格式,这里只需要确保它能正确衔接LoopBack的中间件流程,以下是优化后的完整代码:
import { MiddlewareContext, Next } from '@loopback/rest'; import express from 'express'; import { useApitally } from 'apitally'; // 初始化Apitally的Express路由实例 const apitallyRouter = express(); apitallyRouter.use((req, res, next) => { console.log('[apitally] incoming', req.method, req.path); next(); }); // 配置Apitally核心参数 useApitally(apitallyRouter, { clientId: "CLIENT_ID", env: "prod", logger: { debug: console.debug, info: console.info, warn: console.warn, error: console.error, }, requestLoggingConfig: { enabled: true, logQueryParams: true, logRequestHeaders: true, // logRequestBody: true, logResponseHeaders: true, // logResponseBody: true, }, }); // 适配为LoopBack兼容的中间件 export const apitallyMiddleware = (ctx: MiddlewareContext, next: Next) => { return new Promise<void>((resolve, reject) => { apitallyRouter(ctx.request, ctx.response, (err) => { if (err) return reject(err); resolve(); }); }).then(() => next()); };
2. 将Apitally中间件注册为全局中间件
现在需要把这个中间件注册到LoopBack应用中,让它对所有请求生效。在你的application.ts里添加注册逻辑:
import { apitallyMiddleware } from './path-to-your-middleware-file'; // 替换为实际文件路径 export class YourApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication))) { constructor(options: ApplicationConfig = {}) { super(options); // 注册Apitally为全局中间件(所有请求都会优先经过它) this.middleware(apitallyMiddleware); // ...其他应用配置(比如绑定控制器、数据源等) } }
3. 调整自定义Sequence的执行顺序
你使用了自定义MySequence,之前的问题出在中间件执行顺序不对——你在findRoute之后才手动调用中间件,导致未匹配到路由的请求根本没机会触发Apitally。现在修改MySequence,让全局中间件在路由查找前自动执行:
import { inject } from '@loopback/core'; import { FindRoute, InvokeMethod, ParseParams, Reject, RequestContext, Send, SequenceActions, SequenceHandler, InvokeMiddleware, } from '@loopback/rest'; import { AuthenticationBindings, AuthenticateFn } from '@loopback/authentication'; import { WinstonLogger } from 'winston'; import { LoggingBindings } from './keys'; import { RedisCacheService } from './services'; import { AnalyticsRepository } from './repositories'; export class MySequence implements SequenceHandler { @inject(SequenceActions.INVOKE_MIDDLEWARE, { optional: true }) protected invokeMiddleware: InvokeMiddleware = () => Promise.resolve(false); constructor( @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute, @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams, @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod, @inject(SequenceActions.SEND) public send: Send, @inject(SequenceActions.REJECT) public reject: Reject, @inject(AuthenticationBindings.AUTH_ACTION) protected authenticateRequest: AuthenticateFn, @inject(LoggingBindings.WINSTON_LOGGER) private logger: WinstonLogger, @service() private redisCacheService: RedisCacheService, @repository(AnalyticsRepository) private analyticsRepository: AnalyticsRepository, ) {} async handle(context: RequestContext) { const { request, response } = context; try { // 第一步:优先执行所有全局中间件(包括Apitally) const finished = await this.invokeMiddleware(context); if (finished) return; // 如果中间件已经处理了响应(比如返回404),直接结束流程 // 第二步:执行路由查找、参数解析等核心逻辑 const route = this.findRoute(request); // 移除之前手动调用apitallyMiddleware1的代码,全局注册后无需重复调用 // await this.authenticateRequest(request); const args = await this.parseParams(request, route); const result = await this.invoke(route, args); this.send(response, result); } catch (err: any) { this.reject(context, err); } } }
为什么之前的代码只在Swagger路由生效?
之前你在findRoute之后手动调用Apitally中间件,而LoopBack的findRoute会对不存在的路由直接抛出错误,导致中间件根本没机会执行。而Swagger相关路由是LoopBack内置的,findRoute能成功匹配,所以中间件会触发。
把中间件注册为全局并在Sequence最开始执行,就能确保**所有请求(包括不存在的路由、静态文件请求等)**都会经过Apitally中间件。
内容来源于stack exchange
相关产品推荐
相关产品推荐

