NestJS 怎么实现依靠 URL 前缀 '/admin' 和 '/app' 分别挂载两个不同 Module 从而实现区分 api 合集的逻辑?

2020-06-25 07:22:20 +08:00
 gzlock

目前的 NestJS 项目文件夹结构

libs 中的

// main.js
async function bootstrap () {
  const admin = await NestFactory.create(AdminModule)

  const app = await NestFactory.create(AppModule)

  await Promise.all([
    admin.init(),
    app.init(),
  ])
  await Promise.all([
    admin.listen(3000),
    app.listen(3001),
  ])
}

发帖前也搜过这个需求,nestjs 的 github issue 也有类似的需求,但都被关闭了和不可用

例如2018 年有 25 个👍的回答

// 需要改成这样
// main.ts
async function bootstrap () {
  const server = express()

  const appFactory = new NestFactoryStatic()
  const app = await apiFactory.create(AppModule, new ExpressAdapter(server))
  app.setGlobalPrefix('/app')
  await app.init()

  const adminFactory = new NestFactoryStatic()
  const admin = await adminFactory.create(AdminModule, new ExpressAdapter(server))
  admin.setGlobalPrefix('/admin')
  await admin.init()

  http.createServer(server).listen(3000)
}

我尝试过了,admin 会覆盖掉 app 的路由,失败

搜到一个nest-router,但最后 commit 是 12 个月前,我就没尝试

有没有 NestJS 大佬指教一下该怎么实现

nestjs 官方是不是不推荐这样弄?不然应该很容易实现的。

4292 次点击
所在节点    Node.js
20 条回复
gzlock
2020-06-25 07:27:34 +08:00
@Livid node.js 的分区主题会导致 markdown 代码区域的文字出现黑色背景哎
用“```ts”和“```typescript”开头都试过了,还是一样的效果
gzlock
2020-06-25 07:32:14 +08:00
@gzlock #1 好吧是不支持 ts 或 typescript 样式的问题,改成 javascript 就可以了
Livid
2020-06-25 08:58:15 +08:00
@gzlock 收到。我看一下如何可以支持 ts 。谢谢。
noe132
2020-06-25 09:05:44 +08:00
老哥你是不是误入歧途了。。
Controller 类的 decorator 可以设置 url prefix 的
https://docs.nestjs.com/controllers
建议把 nest 文档看一看
gzlock
2020-06-25 09:47:56 +08:00
我知道的,但我不能将 /app/* 下的所有接口都放在一个 controller 下吧
想把例如 /app/user/*相关的放在一个 module 里
那这个 module controller 的 path 是不是需要写绝对路径为 /app/user ?
@noe132
noe132
2020-06-25 10:03:27 +08:00
cuvii
2020-06-25 10:23:05 +08:00
@gzlock
1. AppModule 通过 setGlobalPrefix('/app'),这样 AppModule 路由就是 /app 了
2.UserModule 里的 Controller 装饰器 @Controller('user')
3.AppModule 里 Import UserModule
这样 UserController 下的路由都是 /app/user/xx 了。
path 不需要写绝对路径。
不知道这样有没有解决你的问题。
cuvii
2020-06-25 10:28:26 +08:00
如果不希望 api 是 /app/api,/app/admin 开头的话,app.setGlobalPrefix('/'),然后 appmodule 里引入其他子模块。
比如 user module 里 @Controller('user'),那路径就是 host/user
cuvii
2020-06-25 10:30:17 +08:00
FakerLeung
2020-06-25 10:30:23 +08:00
@gzlock #5
不用吧。
我现在的接口是
/app1/xxx/yyy
/app2/xxx/yyy
app1 和 app2 都是两个独立的 module 。
而且 app1 下又有 xxx,yyy,zzz 三个 module,这 3 个 module 也是独立出去的。只是每个 controller 又要再写一次 /app1/xxx 这样,感觉麻烦。
gzlock
2020-06-25 16:28:44 +08:00
@FakerLeung #10

@cuvii #7

不使用 library 分开两种逻辑的话,admin 的 module 和 app 的 module 都挤在一个 src 文件夹里,会不会弄得 module 文件夹数量很庞大,也难以查找呢?
cuvii
2020-06-25 16:35:43 +08:00
@gzlock 前面讲的可能有点乱,因为 nest.js 默认的 module 就是 app module 。
你实际上是可以直接创建一个新的 module,例如 main module 来作为容器 module 。
```
const main = await NestFactory.create(MainModule);
main.setGlobalPrefix('/');
```
然后 admin 的逻辑写在 admin module 里,app 的逻辑写在 app module 里,然后把这两个 module 导入到 main module 里就可以了。
至于你说的 module 文件夹数量庞大,我觉得这就看你自己怎么处理文件夹结构了,用 cli 生成的 module 是默认都在 src 下的,你可以根据业务逻辑把 module 放到对应的功能模块下去。
gzlock
2020-06-25 17:30:47 +08:00
@FakerLeung #10

@cuvii #12

刚刚实践了一下 https://github.com/gzlock/nest_js
的确每个 controller 都需要写绝对路径😓,如果可以支持相对路径就真的完美了
不过也解决了需要开两个单独 application 的问题
FakerLeung
2020-06-25 17:46:33 +08:00
@gzlock #13
对的,确实需要写绝对路径,我也在想如何才能在一个顶部的 module 中,写好了 prefix,下面的所有 module 中的所有 controller 都能继承这个 prefix 。
cuvii
2020-06-25 17:52:40 +08:00
@gzlock 好吧,路由这块可能我的理解有问题,你可以看看 https://github.com/nestjsx/nest-router,不知道能不能解决你的问题,我个人感觉用 router 会更麻烦一点...
gzlock
2020-06-25 22:26:10 +08:00
@FakerLeung #14 官方 issue 好像有讨论过这个问题,有人说因为 module 有可能被其他 module 引用所以 module 不适合放 path 参数
FakerLeung
2020-06-25 22:33:17 +08:00
@gzlock #16
谁说都有道理。
kid740246048
2020-06-26 08:55:48 +08:00
关注一波,我也觉得在 module 里面写死绝对路径不方便复用
rikka
2020-11-03 15:05:36 +08:00
遇到同样的问题,搜到这帖,然后自己看了看源码,解决如下

加个装饰器函数
//set-module-prefix.decorator.ts

```ts
import { PATH_METADATA, MODULE_METADATA } from '@nestjs/common/constants'

const { IMPORTS, CONTROLLERS } = MODULE_METADATA

function resolveController (target, controllers = []) {
controllers.push(...(Reflect.getMetadata(CONTROLLERS, target) ?? []))
const imports = Reflect.getMetadata(IMPORTS, target)
if (imports) {
imports.forEach(module => {
resolveController(module, controllers)
})
}
return controllers
}

export function setModulePrefix (prefix:string) {
return target => {
resolveController(target).forEach(controller => {
const path = Reflect.getMetadata(PATH_METADATA, controller)
if (path) {
Reflect.defineMetadata(PATH_METADATA, prefix + '/' + path, controller)
}
})
}
}
````

使用,这样就自动给 AdminModule 下的所有控制器加上`admin`这个前缀了
```ts
import { Module } from '@nestjs/common'
import { AdminController } from './controllers/admin.controller'
import { setModulePrefix } from '../decorators/set-module-prefix.decorator'

@setModulePrefix('admin')
@Module({
imports: [
],
controllers: [
AdminController,
],
providers: [
],
})
export class AdminModule {}

```
cgyrock
306 天前
管理端 admin 和 C 端 customer 分别创建一个应用,可以独立启动,然后创建一个 monoapp 应用,导入这两个应用的根模块。两个应用的跟模块使用动态模块,接收一个 context 参数作为统一前缀,用官方的 RouteModule 来为所有子模块添加统一前缀

monoapp: http://picbed.catfoodworks.com/WechatIMG522.jpeg
admin: http://picbed.catfoodworks.com/WechatIMG523.jpeg

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/684614

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX