资讯动态

将 LoopBack 3 应用挂载到 LoopBack 4 项目:基于 booter-lb3app 的渐进式迁移完整指南

发布时间:2026/10/14 20:27:41 来源:尧图企业网站定制
后端微服务【免费下载链接】loopback-nextLoopBack makes it easy to build modern API applications that require complex integrations.项目地址https://gitcode.com/gh_mirrors/lo/loopback-next点击查看免费下载导读本文基于 LoopBack 官方示例loopback/example-lb3-applicationexamples/lb3-application系统讲解如何把已有的 LoopBack 3LB3应用挂载到全新的 LoopBack 4LB4项目上并进一步把 Express 中间件迁移到公共位置、让 LB3 与 LB4 共享最后实现在 LB4 项目中直接运行 LB3 风格的验收测试与集成测试。读完本文你将掌握一条LB3 应用原地复用、LB4 能力渐进接管的平滑迁移路径无需重写旧应用即可让新老两套代码在同一进程中协同对外提供服务。一、为什么需要挂载而非重写LoopBack 3 与 LoopBack 4 是两代架构完全不同的框架LB3 以 JavaScript 编写、依赖loopback-boot运行时引导而 LB4 是 TypeScript 优先、基于依赖注入与装饰器的新架构。对于已经拥有大量 LB3 模型、boot 脚本和 REST 接口的存量应用一次性重写成本高、风险大。loopback/booter-lb3app包提供了一条渐进式路径在 LB4 应用启动时通过 Booter 加载并引导 LB3 应用把它的 Swagger 规范转换为 OpenAPI v3 规范再把整个 LB3 应用连同规范挂载到 LB4 应用的 Express 路由器上从而让旧接口原样照常运行同时新功能可以逐步用 LB4 的方式开发。该包的定位在其 README 中有明确说明Boot a LoopBack 3 application and expose its REST API via LoopBack 4.官方示例loopback/example-lb3-application正是这条路径的完整可运行演示它包含一个真实的 LB3 应用CoffeeShop 咖啡馆模型 用户认证一个 LB4 应用CoffeeShopApplication以及一个将它们组装起来的 Express 服务器。二、第一步把 LB3 应用挂载到 LB4 项目这一节对应 README 的 Mounting LB3 app on LB4 app 章节是整条迁移路径的起点。以下是完整操作步骤。2.1 创建 LB4 项目并放入 LB3 应用使用 CLI 创建新的 LoopBack 4 项目$ lb4 app按提示填写项目信息保留所有 feature 默认启用。从 LB4 项目根目录新建lb3app/目录把现有 LB3 应用整体复制进去。最终目录布局应如下lb3app/ # LoopBack 3 application in JavaScript common/ models/ # LB3 model files server/ boot/ # LB3 boot scripts public/ # front-end assets (LB4 way) src/ # LoopBack 4 application in TypeScript示例仓库中lb3app/server/server.js 是标准的 LB3 入口创建loopback()实例后调用boot(app, __dirname, ...)引导应用、装配模型、数据源与中间件。2.2 把 LB3 依赖并入主 package.json删除lb3app/package.json、lb3app/node_modules/以及若存在lb3app/package-lock.json将 LB3 运行时依赖移动到 LB4 项目的package.json中。典型的最小依赖集如下此外还需按实际使用情况补上各类 connector 与 component{ compression: ^1.7.4, cors: ^2.8.5, helmet: ^3.16.0, loopback: ^3.25.1, loopback-boot: ^3.3.0 }注意loopback-boot必须使用 3.2.1 或更高版本。这一点在Lb3AppBooter的源码中也有对应的运行时提示packages/booter-lb3app/src/lb3app.booter.ts如果 LB3 应用未能正确引导首先检查它是否使用了 3.2.1 以上的loopback-boot。版本过低时boot()回调的时序问题会导致挂载失败。随后在 LB4 项目根目录执行npm install安装这些依赖。当前仓库示例实际使用的版本更高见 examples/lb3-application/package.jsonloopback^3.28.0、loopback-boot^3.3.1、helmet^4.6.0、compression^1.8.2、cors^2.8.6可作为升级参考。2.3 禁用 LB3 的错误处理交给 LB4错误处理统一由新架构负责避免新旧两层重复拦截删除lb3app/server/middleware.development.json编辑lb3app/server/middleware.json移除以下两个条目final下的loopback#urlNotFoundfinal:after下的strong-error-handler从package.json依赖中移除strong-error-handler在lb3app/server/config.json中若remoting里已有handleErrors: false把它移动到rest属性下remoting.rest。2.4 静态资源交由 LB4 提供把lb3app/client下的前端文件移动到 LB4 项目的public/目录在lb3app/server/middleware.json中移除静态资源中间件条目files下的loopback#static删除lb3app/server/boot/root.js因为主页将由 LB4 项目负责提供。示例仓库的 public/index.html 即同时包含 LB4 首页与保留的lb3-index.html旧首页。2.5 移除 LB3 的 API Explorer删除lb3app/server/component-config.json禁用 LB3 自带的 explorer统一改用 LB4 的 explorer示例中通过RestExplorerComponent提供见 src/application.ts。2.6 安装并注册 loopback/booter-lb3app安装依赖npm install --save loopback/booter-lb3app在src/application.ts顶部引入组件import {Lb3AppBooterComponent} from loopback/booter-lb3app;在 Application 构造函数中注册组件this.component(Lb3AppBooterComponent);组件本身极其轻量其实现就是把Lb3AppBooter注册进 booters 列表见 lb3app.booter.component.tsexport class Lb3AppBooterComponent implements Component { booters [Lb3AppBooter]; }2.7 启动验证$ npm start Server is running at http://127.0.0.1:3000打开控制台输出的地址查看public/提供的前端页面或访问http://127.0.0.1:3000/explorer浏览 REST API。至此LB3 应用已成功挂载在 LB4 应用之上——但你还可以进一步优化把几乎全部中间件迁移到公共位置让新旧两代应用共享。三、挂载的底层原理Lb3AppBooter 做了什么在进入中间件迁移之前先看挂载动作在源码层面究竟如何发生。Lb3AppBooter的load()方法packages/booter-lb3app/src/lb3app.booter.ts依次完成四件事加载并引导 LB3 应用loadAndBootTheApp()通过require()载入lb3app/server/server.js若lb3App.booting为真则等待booted事件确保引导完成lb3app.booter.ts生成并转换 OpenAPI 规范buildOpenApiSpec()用loopback-swagger从 LB3 应用生成 Swagger 2.0 规范再经swagger2openapi转换为 OpenAPI v3 规范并支持通过specTransformer选项自定义转换lb3app.booter.ts挂载 LB3 应用按mode选择挂载方式见下把 LB3 的数据源与模型绑定到 LB4 上下文lb3-datasources.{ds name}与lb3-models.{model name}lb3app.booter.ts这正是后续从 LB4 运行 LB3 集成测试时能通过get()取到 LB3 模型的前提。挂载分为两种模式Lb3AppBooterOptions.mode默认选项见 lb3app.booter.ts模式行为关键代码fullApp默认把整个 LB3 应用作为 Express 应用挂载到其restApiRoot来自 LB3config.json并使用rebaseOpenApiSpec按根路径重定位规范mountFullApplb3app.booter.tsrestRouter仅挂载 LB3 的 REST 处理器到restApiRoot选项指定的路径默认/apimountRoutesOnlylb3app.booter.ts最终挂载调用的是 LB4RestApplication.mountExpressRouter(basePath, router, spec)APIpackages/rest/src/rest.application.ts该 API 会把 Express 路由注册进 LB4 REST 服务器并同步进 OpenAPI 规范。四、第二步把 Express 中间件迁移到公共位置挂载成功后LB3 应用内仍残留着大量 Express 中间件favicon、compression、cors、helmet 等。本节按照 README 的 Migrating Express middleware from LB3 app 章节把它们从 LB3 的middleware.json迁移到公共的 Express 应用中让 LB3 与 LB4 共享同一份中间件配置。4.1 更新 LB3 的 config.json编辑lb3app/server/config.json移除已不再需要的host与port监听交由 Express 应用负责host: 0.0.0.0, port: 3000,把restApiRoot改为/或你希望挂载 LB3 应用的任意路径——该路径相对 LB4 REST API 路径默认/api而言为rest属性新增handleUnknownPaths: false防止 LB3 REST API 对无法处理的请求返回 404。迁移后的config.json应与示例仓库中的 lb3app/server/config.json 一致{ restApiRoot: /, remoting: { context: false, rest: { handleErrors: false, handleUnknownPaths: false, normalizeHttpPath: false, xml: false }, json: { strict: false, limit: 100kb }, urlencoded: { extended: true, limit: 100kb }, cors: false } }各参数作用context: false关闭 remoting contexthandleErrors: false与handleUnknownPaths: false把错误处理与未知路径处理全部交给外层normalizeHttpPath: false保持路径原样xml: false禁用 XML 响应json/urlencoded下的strict、limit、extended控制请求体解析行为cors: false说明 CORS 已由外层公共中间件负责。4.2 配置基础 Express 应用src/server.ts思路是创建一个基础 Express 应用作为总入口把 LB4 应用挂载为子路由同时承载从 LB3 迁移过来的全部中间件。仓库内对应的完整教程参见 docs/site/express-with-lb4-rest-tutorial.md。middleware.json中每个根属性代表一个中间件阶段按顺序在 Express 应用中加载。例如compression: {}对应compression()loopback#favicon对应loopback.favicon()。迁移后LB3 的middleware.json只剩下 REST 路由本身lb3app/server/middleware.json{ routes: { loopback#rest: { paths: [ ${restApiRoot} ] } } }注意routes阶段的中间件暂不迁移README 中说明这是待完成事项仅保留loopback#rest路由条目。LB3 应用中的静态文件移动到 Express 应用的public/目录任何非 REST 路由也移动到 Express 应用。最终src/server.ts与示例仓库的 src/server.ts 一致import {ApplicationConfig} from loopback/core; import {once} from events; import express, {Request, Response} from express; import * as http from http; import {AddressInfo} from net; import * as path from path; // Replace CoffeeShopApplication with the name of your application import {CoffeeShopApplication} from ./application; const loopback require(loopback); const compression require(compression); const cors require(cors); const helmet require(helmet); export class ExpressServer { private app: express.Application; public readonly lbApp: CoffeeShopApplication; public server?: http.Server; public url: String; constructor(options: ApplicationConfig {}) { this.app express(); this.lbApp new CoffeeShopApplication(options); // Middleware migrated from LoopBack 3 this.app.use(loopback.favicon()); this.app.use(compression()); this.app.use(cors()); this.app.use(helmet()); // Mount the LB4 REST API this.app.use(/api, this.lbApp.requestHandler); // Custom Express routes this.app.get(/ping, function (_req: Request, res: Response) { res.send(pong); }); // Serve static files in the public folder this.app.use(express.static(path.join(__dirname, ../public))); } public async boot() { await this.lbApp.boot(); } public async start() { await this.lbApp.start(); const port this.lbApp.restServer.config.port || 3000; const host this.lbApp.restServer.config.host || 127.0.0.1; this.server this.app.listen(port, host); await once(this.server, listening); const add AddressInfothis.server.address(); this.url http://${add.address}:${add.port}; } public async stop() { if (!this.server) return; await this.lbApp.stop(); this.server.close(); await once(this.server, close); this.server undefined; } }代码要点中间件顺序favicon → compression → cors → helmet依次挂载在this.app上随后才挂载 LB4 REST API/api前缀、自定义/ping路由与静态目录符合 Express 中间件先通用后业务的执行顺序LB4 以路由形式接入通过this.app.use(/api, this.lbApp.requestHandler)把 LB4 应用暴露为/api下的子路由端口与主机读取lbApp.restServer.config的port/host缺省回退到3000与127.0.0.1生命周期boot()/start()/stop()均委托给内部的lbApp并用once()等待listening/close事件保证异步时序。这些从 LB3middleware.json迁移而来的中间件现在被 LB3 与 LB4 应用共享。4.3 更新 src/index.ts 入口Express 应用取代CoffeeShopApplication成为程序入口修改src/index.ts与示例仓库的 src/index.ts 一致import {ApplicationConfig} from loopback/core; import {ExpressServer} from ./server; export {ApplicationConfig, ExpressServer}; export async function main(options: ApplicationConfig {}) { const server new ExpressServer(options); await server.boot(); await server.start(); console.log(Server is running at ${server.url}); }4.4 阻止 LB4 应用自行监听由于现在由 Express 应用统一监听端口LB4 应用必须停止自行监听。在src/index.ts的启动配置中给rest加上listenOnStart: falseconst config { rest: { port: (process.env.PORT ?? 3000), host: process.env.HOST ?? localhost, openApiSpec: { // useful when used with OpenAPI-to-GraphQL to locate your application setServersFromRequest: true, }, listenOnStart: false, }, };openApiSpec.setServersFromRequest: true表示 OpenAPI 规范中的 server 地址取自实际请求便于与 OpenAPI-to-GraphQL 等工具配合。随后在CoffeeShopApplication类的bootOptions中配置lb3app的挂载路径与模式。bootOptions最终形态与示例仓库的 src/application.ts 一致this.bootOptions { controllers: { // Customize ControllerBooter Conventions here dirs: [controllers], extensions: [.controller.js], nested: true, }, lb3app: { mode: fullApp, }, };mode: fullApp告诉 Booter 把整个 LB3 应用而非仅 REST 路由挂载上来与第三节中mountFullApp的行为对应。4.5 启动并验证$ npm start浏览器访问http://localhost:3000/即可看到同时挂载了 LB3 与 LB4 应用的 Express 应用。示例中/返回 LB4 首页/api下是 LB4 REST API其中包含来自 LB3 的CoffeeShops等接口/ping返回pong/lb3-index.html保留旧 LB3 首页——这些行为均有对应验收测试home-page.acceptance.ts、lb3app.acceptance.ts逐一断言。五、第三步从 LB4 项目运行 LB3 测试挂载与中间件迁移完成后还可以让旧的 LB3 测试继续在 LB4 项目中运行。目标是让 LB3 测试使用 LB4 服务器而非 LB3 应用本身覆盖两类测试验收测试acceptance-level通过 HTTP 调用应用逻辑例如POST /users/login集成测试integration-level通过 JS API 调用应用逻辑例如MyModel.create()。5.1 把 LB3 测试路径加入 test 命令在package.json的test条目中追加 LB3 测试目录。示例仓库实际使用的命令是见 examples/lb3-application/package.jsontest: lb-mocha \dist/__tests__/**/*.js\ \lb3app/test/*.js\该命令先用lb-mocha运行编译后的 LB4 测试dist/__tests__/再运行lb3app/test/下的 LB3 测试。示例仓库的 LB3 测试包括 acceptance.js、authentication.js 与 integration.js。5.2 运行验收测试首先把 LB3 测试依赖移动到package.json的devDependencies并执行npm install。然后在测试文件中按启动方式二选一改造。方式一启动 Express 服务器请求辅助函数改为直接针对 Express 服务器注意 LB3 API 挂在基路径/api下// can use lb4s testlabs supertest as the dependency is already installed const {supertest} require(loopback/testlab); const assert require(assert); const should require(should); const {ExpressServer} require(../../dist/server); let app; function jsonForExpressApp(verb, url) { // use the express server, it mounts LoopBack 3 apis to // base path /api return supertest(app.server) verb .set(Content-Type, application/json) .set(Accept, application/json) .expect(Content-Type, /json/); }在before钩子中 boot start在after钩子中 stopdescribe(LoopBack 3 style tests - Launch Express server, function () { before(async function () { app new ExpressServer(); await app.boot(); await app.start(); }); after(async () { await app.stop(); }); // your tests here runTests(); });方式二直接启动 LB4 应用请求辅助函数改用 LB4 应用的 REST 服务器路径无需/api前缀// can use lb4s testlabs supertest as the dependency is already installed const {supertest} require(loopback/testlab); const assert require(assert); const should require(should); const {CoffeeShopApplication} require(../../dist/application); let app; function jsonForLB4(verb, url) { // use the lb4 apps rest server return supertest(app.restServer.url) verb .set(Content-Type, application/json) .set(Accept, application/json) .expect(Content-Type, /json/); }生命周期钩子describe(LoopBack 3 style tests - launch LB4 app, function () { before(async function () { app new CoffeeShopApplication(); await app.boot(); await app.start(); }); after(async () { await app.stop(); }); // your tests here runTests(); });这两种写法的完整示例就是 lb3app/test/acceptance.js——它同时包含从 Express 服务器引导与从 LB4 应用引导两个 describe 块且测试用例与 LB4 风格的 src/tests/acceptance/lb3app.acceptance.ts 完全对应便于对照学习。现在运行npm testLB3 测试就会与 LB4 测试一同执行。可选方案也可以把旧测试逐步改写成 LB4 风格的测试参考 lb3app.acceptance.ts仓库内亦有测试方法论文档 docs/site/Testing-your-application.md 可参考。5.3 运行集成测试集成测试需要直接调用 LB3 模型的方法如CoffeeShop.create()。得益于第三节中提到的绑定机制LB3 模型已通过lb3-models.{model name}键绑定到 LB4 应用上下文用get()即可取出describe(LoopBack 3 style integration tests, function () { let app; let CoffeeShop; before(async function () { // If launch the LoopBack 4 application // app new CoffeeShopApplication(); app new ExpressServer(); await app.boot(); await app.start(); }); before(() { // follow the syntax: lb3-models.{ModelName} // If launch the LoopBack 4 application // CoffeeShop await app.get(lb3-models.CoffeeShop); CoffeeShop await app.lbApp.get(lb3-models.CoffeeShop); }); after(async () { await app.stop(); }); // your tests here runTests(); });LB3 模型的绑定键语法lb3-models.{model name}LB3 数据源同样绑定到 LB4 上下文键语法lb3-datasources.{ds name}。模型与数据源的绑定实现位于 packages/booter-lb3app/src/lb3app.booter.ts注意其中使用visitedDs/visitedModels集合去重避免共享实例被重复绑定。完整的集成测试示例见 lb3app/test/integration.js它展示了CoffeeShop.find、CoffeeShop.count、CoffeeShop.create等典型用例认证相关的 LB3 测试见 lb3app/test/authentication.js。六、示例项目中的可复现细节为帮助读者把上文步骤映射到真实代码这里汇总示例仓库的关键文件及其作用文件作用lb3app/server/server.jsLB3 入口创建 app 并调用loopback-boot引导lb3app/server/config.json迁移后的 LB3 配置restApiRoot、remoting 各项lb3app/server/middleware.json迁移后仅保留loopback#rest路由lb3app/server/boot/create-sample-models.jsLB3 boot 脚本自动建表并写入示例数据lb3app/server/boot/authentication.jsLB3 boot 脚本调用server.enableAuth()开启认证lb3app/common/models/coffee-shop.jsLB3 模型含status/greet两个 remote methodsrc/application.tsLB4 应用注册Lb3AppBooterComponent并配置lb3app.bootOptionssrc/server.tsExpress 服务器承载共享中间件并挂载 LB4/LB3src/index.ts入口listenOnStart: false配置lb3app/test/acceptance.jsLB3 风格验收测试两种启动方式lb3app/test/integration.jsLB3 风格集成测试模型方法调用示例的 CoffeeShop 模型同时演示了认证与 ACL 的迁移效果coffee-shop.json中为greet方法配置了 ACL拒绝$unauthenticated角色访问coffee-shop.json因此 lb3app.acceptance.ts 中断言匿名访问/api/CoffeeShops/greet返回 401而携带access_token的请求返回 200。此外示例项目还提供了两个辅助脚本npm run migrate执行 migrate.ts通过--rebuild参数决定 drop 还是 alter 现有 schemanpm run openapi-spec执行 openapi-spec.ts把合并后的 OpenAPI v3 规范导出到文件。关于 OpenAPI 规范的合并lb3app.acceptance.ts 中有完整断言LB3 的 Swagger 规范经转换后以 OpenAPI 3.0.0 形式并入 LB4 规范CoffeeShop、User等 schema、requestBodies、paths如/Users/login、/CoffeeShops以及basePath均被正确迁移。七、常见问题与注意事项LB3 应用没有正确引导确认loopback-boot版本 ≥ 3.2.1Booter 源码 lb3app.booter.ts 有相应提示并检查lb3app/server/server.js是否正确导出 app 实例。端口冲突或双重监听迁移到 Express 服务器后务必在config.rest中设置listenOnStart: false否则 LB4 应用会与 Express 服务器争抢端口。404 由谁返回为 LB3 的rest配置设置handleUnknownPaths: false并移除final/final:after中的loopback#urlNotFound与strong-error-handler让错误处理统一由 LB4/Express 层负责。中间件顺序从middleware.json迁移中间件时必须按原阶段顺序favicon → compression → cors → helmet → 业务路由 → 静态资源在 Express 应用中排列。routes阶段中间件暂不支持迁移README 明确指出这是待办事项对应 upstream issue #4181目前仅保留loopback#rest路由。获取 LB3 模型/数据源集成测试中统一通过lb3-models.{name}、lb3-datasources.{name}绑定键从 LB4 上下文取用不要直接requireLB3 内部文件。八、总结本文完整呈现了LB3 应用挂载到 LB4 项目的三步走路径挂载通过loopback/booter-lb3app在 LB4 应用启动时引导 LB3 应用、转换 OpenAPI 规范并挂载其 REST API同时把模型与数据源绑定到 LB4 上下文中间件迁移借助基础 Express 应用承载favicon、compression、cors、helmet等公共中间件让新旧两代应用共享LB3 的middleware.json退化为仅剩loopback#rest测试复用修改test命令把lb3app/test纳入执行范围验收测试与集成测试分别通过 Express 服务器/LB4 应用两种方式运行旧测试资产得以完整保留。这条路径让团队可以新功能用 LB4 写、旧接口零改动继续跑是 LoopBack 3 存量应用向 LoopBack 4 渐进迁移的官方推荐做法。完整可运行示例就在仓库的 examples/lb3-application 中核心 Booter 实现位于 packages/booter-lb3app/src/lb3app.booter.ts读者可以在此基础上对照源码继续深入。赞分享后端微服务【免费下载链接】loopback-nextLoopBack makes it easy to build modern API applications that require complex integrations.项目地址https://gitcode.com/gh_mirrors/lo/loopback-next点击查看免费下载相关推荐在 LoopBack 4 中挂载 LoopBack 3 应用loopback/booter-lb3app 实战指南在 LoopBack 4 中挂载 LoopBack 3 应用loopback/booter lb3app 实战指南 导读 LoopBack 3LB3与后端微服务将 LoopBack 3 组件中的 Model Mixin 迁移到 LoopBack 4三步迁移指南将 LoopBack 3 组件中的 Model Mixin 迁移到 LoopBack 4三步迁移指南 本文聚焦于一个具体迁移场景如何把以提供 Model后端微服务LoopBack 3 到 LoopBack 4 数据源Datasource迁移完整指南LoopBack 3 到 LoopBack 4 数据源Datasource迁移完整指南 本篇指南以官方迁移文档 datasources.md https:/后端微服务上一篇wvp-GB28181-pro视频监控平台容器化部署技术解析下一篇Android RecyclerView扩展组件下拉刷新与上拉加载更多的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑